Compare commits

..

1 Commits

Author SHA1 Message Date
guofei 5415e2c007 1 2025-01-13 17:36:58 +08:00
13 changed files with 279 additions and 1578 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -1,205 +0,0 @@
const sharp = require('sharp');
const fs = require('fs').promises;
const path = require('path');
const ffmpeg = require('fluent-ffmpeg');
// 支持的图片格式
const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.webp'];
const AUDIO_EXTENSIONS = ['.mp3'];
// 压缩配置
const COMPRESSION_OPTIONS = {
jpeg: {
quality: 60, // 降低质量以获得更高压缩率
mozjpeg: true, // 使用 mozjpeg 压缩
chromaSubsampling: '4:2:0' // 更激进的色度采样
},
png: {
quality: 60, // 降低质量
compressionLevel: 9, // 最高压缩级别
palette: true, // 使用调色板模式
effort: 10, // 最大压缩效果
colors: 128 // 减少调色板颜色数量以增加压缩率
},
webp: {
quality: 60, // 降低质量
effort: 6, // 最高压缩效果
lossless: false, // 有损压缩
nearLossless: false // 关闭近无损压缩
}
};
// 格式化文件大小
function formatFileSize(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';
return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
}
async function processDirectory(inputDir) {
try {
const items = await fs.readdir(inputDir);
let totalOriginalSize = 0;
let totalCompressedSize = 0;
for (const item of items) {
const filePath = path.join(inputDir, item);
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
await processDirectory(filePath);
} else if (stats.isFile()) {
const ext = path.extname(item).toLowerCase();
const originalSize = stats.size;
if (IMAGE_EXTENSIONS.includes(ext)) {
await compressImage(filePath);
} else if (AUDIO_EXTENSIONS.includes(ext)) {
await compressAudio(filePath);
} else {
continue;
}
const compressedStats = await fs.stat(filePath);
const compressedSize = compressedStats.size;
totalOriginalSize += originalSize;
totalCompressedSize += compressedSize;
const savedSize = originalSize - compressedSize;
const savingPercentage = ((savedSize / originalSize) * 100).toFixed(2);
console.log(`已压缩: ${filePath}`);
console.log(` 原始大小: ${formatFileSize(originalSize)}`);
console.log(` 压缩后大小: ${formatFileSize(compressedSize)}`);
console.log(` 节省: ${formatFileSize(savedSize)} (${savingPercentage}%)`);
console.log('----------------------------------------');
}
}
if (totalOriginalSize > 0) {
const totalSaved = totalOriginalSize - totalCompressedSize;
const totalSavingPercentage = ((totalSaved / totalOriginalSize) * 100).toFixed(2);
console.log(`\n当前目录 ${inputDir} 总计:`);
console.log(` 原始总大小: ${formatFileSize(totalOriginalSize)}`);
console.log(` 压缩后总大小: ${formatFileSize(totalCompressedSize)}`);
console.log(` 总共节省: ${formatFileSize(totalSaved)} (${totalSavingPercentage}%)\n`);
}
} catch (error) {
console.error('处理出错:', error);
}
}
async function compressImage(filePath) {
const ext = path.extname(filePath).toLowerCase();
const tempPath = filePath + '.temp';
const image = sharp(filePath);
try {
// 获取图片信息
const metadata = await image.metadata();
// 根据图片大小和类型选择压缩策略
switch (ext) {
case '.jpg':
case '.jpeg':
await image
.jpeg(COMPRESSION_OPTIONS.jpeg)
.toFile(tempPath);
break;
case '.png':
// 如果PNG是透明的保持PNG格式
if (metadata.hasAlpha) {
await image
.png(COMPRESSION_OPTIONS.png)
.toFile(tempPath);
} else {
// 如果PNG不是透明的转换为JPEG可能会得到更好的压缩效果
await image
.jpeg(COMPRESSION_OPTIONS.jpeg)
.toFile(tempPath);
}
break;
case '.webp':
await image
.webp(COMPRESSION_OPTIONS.webp)
.toFile(tempPath);
break;
}
// 检查压缩后的文件大小
const originalStats = await fs.stat(filePath);
const compressedStats = await fs.stat(tempPath);
// 只有当压缩后的文件更小时才替换
if (compressedStats.size < originalStats.size) {
await fs.unlink(filePath);
await fs.rename(tempPath, filePath);
console.log(` 压缩成功: ${formatFileSize(originalStats.size)} -> ${formatFileSize(compressedStats.size)}`);
} else {
// 如果压缩后反而更大,则删除临时文件保留原文件
await fs.unlink(tempPath);
console.log(` 跳过压缩: 原始大小 ${formatFileSize(originalStats.size)}, 压缩后 ${formatFileSize(compressedStats.size)}`);
console.log(` 原因: 压缩后文件更大,已保留原文件`);
}
} catch (error) {
try {
await fs.unlink(tempPath);
} catch (e) {
// 忽略临时文件删除失败的错误
}
throw error;
}
}
async function compressAudio(filePath) {
const tempPath = filePath + '.temp';
return new Promise((resolve, reject) => {
ffmpeg(filePath)
.toFormat('mp3')
.audioBitrate(64) // 降低到64kbps以获得最大压缩
.audioChannels(1) // 转换为单声道
.audioFrequency(22050) // 降低采样率
.audioCodec('libmp3lame') // 使用LAME编码器
.addOptions([
'-q:a 9', // 最高压缩质量
'-compression_level 9' // 最高压缩级别
])
.on('end', async () => {
try {
// 检查压缩后的文件大小
const originalStats = await fs.stat(filePath);
const compressedStats = await fs.stat(tempPath);
// 只有当压缩后的文件更小时才替换
if (compressedStats.size < originalStats.size) {
await fs.unlink(filePath);
await fs.rename(tempPath, filePath);
console.log(` 压缩成功: ${formatFileSize(originalStats.size)} -> ${formatFileSize(compressedStats.size)}`);
} else {
await fs.unlink(tempPath);
console.log(` 跳过压缩: 原始大小 ${formatFileSize(originalStats.size)}, 压缩后 ${formatFileSize(compressedStats.size)}`);
console.log(` 原因: 压缩后文件更大,已保留原文件`);
}
resolve();
} catch (error) {
reject(error);
}
})
.on('error', (err) => {
reject(err);
})
.save(tempPath);
});
}
// 设置输入目录
const INPUT_DIR = './assets';
// 运行脚本
(async () => {
console.log('开始处理图片压缩...');
await processDirectory(INPUT_DIR);
console.log('图片压缩完成!');
})();

View File

@ -1,24 +0,0 @@
import * as fs from 'fs'
import * as path from 'path'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const dist = path.join(__dirname, '../', 'dist')
export function removeDist() {
const isExists = fs.existsSync(dist)
if (isExists) {
fs.rmdirSync(dist, { recursive: true })
}
}
export function createDist() {
const isExists = fs.existsSync(dist)
if (!isExists) {
fs.mkdirSync(dist)
}
}

View File

@ -1,132 +1,80 @@
import * as fs from "fs"; const fs = require("fs");
import * as path from "path"; const path = require("path");
import do_task from "./single-html/build.js"; const do_task = require("./single-html/build");
import { createDist } from "./common/utils.js";
const outputPrefix = '';
const htmlChannel = ['applovin', 'unity', 'appier', 'ironsource', 'mintegral', 'moloco'];
// 创建 dist 目录
if (!fs.existsSync('dist')) {
fs.mkdirSync('dist');
}
// 处理 HTML 渠道 // 处理 HTML 渠道
async function processHtmlChannels(options) { async function processHtmlChannels() {
const outputPrefix = options.outputPrefix || "";
const htmlChannel = options.htmlChannel || [
"applovin",
"unity",
"appier",
"ironsource",
"mintegral",
"moloco",
];
for (const channelName of htmlChannel) { for (const channelName of htmlChannel) {
// 遍历 networks 目录下的所有js文件,获取 文件名 然后删除 web-mobile 目录下的同名文件 // 删除所有可能存在的旧文件
const files = fs.readdirSync(path.join("networks")); const filesToDelete = ['mraid_support.js', 'mraid.js', 'mintegral.js', 'moloco.js'];
for (const file of files) { filesToDelete.forEach(file => {
if (file.endsWith(".js")) { const filePath = path.join('web-mobile', file);
const destinationFile = path.join("web-mobile", file); if (fs.existsSync(filePath)) {
if (fs.existsSync(destinationFile)) { fs.unlinkSync(filePath);
fs.unlinkSync(destinationFile); console.log(`已删除: ${filePath}`);
console.log(`已删除: ${destinationFile}`);
}
}
}
// tiktok 处理
const tiktokConfig = path.join("web-mobile", `config.json`);
if (fs.existsSync(tiktokConfig)) {
fs.unlinkSync(tiktokConfig);
}
const tiktokJs = path.join("web-mobile", `tiktok.js`);
if (fs.existsSync(tiktokJs)) {
fs.unlinkSync(tiktokJs);
} }
});
// 从 networks 找到复制到 web-mobile 中 // 从 networks 找到复制到 web-mobile 中
if (["applovin", "unity", "appier", "ironsource"].includes(channelName)) { const mraidSupportFile = path.join('networks', 'mraid_support.js');
const mraidSupportFile = path.join("networks", "mraid_support.js"); const mraidDestinationFile = path.join('web-mobile', 'mraid_support.js');
const mraidDestinationFile = path.join("web-mobile", "mraid_support.js");
if (['applovin', 'unity', 'appier', 'ironsource'].includes(channelName)) {
if (fs.existsSync(mraidSupportFile)) { if (fs.existsSync(mraidSupportFile)) {
fs.copyFileSync(mraidSupportFile, mraidDestinationFile); fs.copyFileSync(mraidSupportFile, mraidDestinationFile);
console.log(`已复制: ${mraidSupportFile}${mraidDestinationFile}`); console.log(`已复制: ${mraidSupportFile}${mraidDestinationFile}`);
} }
} else if (["mintegral", "moloco"].includes(channelName)) { }
if (['mintegral', 'moloco'].includes(channelName)) {
const fileName = `${channelName}.js`; const fileName = `${channelName}.js`;
const sourceFile = path.join("networks", fileName); const sourceFile = path.join('networks', fileName);
const destFile = path.join("web-mobile", fileName); const destFile = path.join('web-mobile', fileName);
if (fs.existsSync(sourceFile)) { if (fs.existsSync(sourceFile)) {
fs.copyFileSync(sourceFile, destFile); fs.copyFileSync(sourceFile, destFile);
console.log(`已复制: ${sourceFile}${destFile}`); console.log(`已复制: ${sourceFile}${destFile}`);
} }
} }
if (["facebook", "google", "tiktok"].includes(channelName)) { const htmlFilePath = path.join('web-mobile', 'index.html');
if (channelName == "tiktok") { let htmlContent = fs.readFileSync(htmlFilePath, 'utf-8');
fs.copyFileSync(
path.join("facebook/config.json"),
path.join("web-mobile", `config.json`)
);
fs.copyFileSync(
path.join("tiktok/tiktok.js"),
path.join("web-mobile", `tiktok.js`)
);
} else {
const sourceFile = path.join("networks", `${channelName}.js`);
const destinationFile = path.join("web-mobile", `${channelName}.js`);
console.log("------------------------", sourceFile);
if (fs.existsSync(sourceFile)) {
fs.copyFileSync(sourceFile, destinationFile);
console.log(`已复制: ${sourceFile}${destinationFile}`);
}
}
}
const htmlFilePath = path.join("web-mobile", "index.html");
// 读取 index.html 文件
let htmlContent = fs.readFileSync(htmlFilePath, "utf-8");
// 移除非当前渠道包的 <script> 标签 // 移除非当前渠道包的 <script> 标签
htmlChannel.forEach((channel) => { htmlChannel.forEach(channel => {
if (channel !== channelName) { if (channel !== channelName) {
const scriptRegex = new RegExp( const scriptRegex = new RegExp(`<script src="${channel}.js" charset="utf-8"></script>\\n`, 'g');
`<script src="${channel}.js" charset="utf-8"></script>\\n`, htmlContent = htmlContent.replace(scriptRegex, '');
"g"
);
htmlContent = htmlContent.replace(scriptRegex, "");
} }
}); });
// 移除 mraid_support.js 和 mraid.js 的 <script> 标签 // 移除 mraid_support.js 和 mraid.js 的 <script> 标签
htmlContent = htmlContent.replace( htmlContent = htmlContent.replace(/<script src="mraid_support.js" charset="utf-8"><\/script>\n/g, '');
/<script src="mraid_support.js" charset="utf-8"><\/script>\n/g, htmlContent = htmlContent.replace(/<script src="mraid.js"><\/script>\n/g, '');
""
);
htmlContent = htmlContent.replace(
/<script src="mraid.js"><\/script>\n/g,
""
);
// 检查并插入新的渠道 script 标签 // 检查并插入新的渠道 script 标签
let channelScriptTag = ''; let channelScriptTag;
if (["applovin", "unity", "appier", "ironsource"].includes(channelName)) { if (['applovin', 'unity', 'appier', 'ironsource'].includes(channelName)) {
channelScriptTag = ` <script src="mraid_support.js" charset="utf-8"></script>\n`; channelScriptTag = ` <script src="mraid_support.js" charset="utf-8"></script>\n`;
} else { } else {
// @TODO
if (!channelName == "facebook") {
channelScriptTag = ` <script src="${channelName}.js" charset="utf-8"></script>\n`; channelScriptTag = ` <script src="${channelName}.js" charset="utf-8"></script>\n`;
} }
}
if (!htmlContent.includes(channelScriptTag)) { if (!htmlContent.includes(channelScriptTag)) {
htmlContent = htmlContent.replace( htmlContent = htmlContent.replace(/<\/head>/, `${channelScriptTag}</head>`);
/<\/head>/,
`${channelScriptTag}</head>`
);
} }
// 替换包含 window.vConsole = new VConsole 的 <script> 标签, 引用 cocos2d-js-min.js // 替换包含 window.vConsole = new VConsole 的 <script> 标签
const vConsoleScriptRegex = const vConsoleScriptRegex = /<script type="text\/javascript">[\s\S]*?window\.vConsole = new VConsole\(\);[\s\S]*?<\/script>/;
/<script type="text\/javascript">[\s\S]*?window\.vConsole = new VConsole\(\);[\s\S]*?<\/script>/;
// @TODO cocos的名称需要从web-mobile文件夹中获取找到 // @TODO cocos的名称需要从web-mobile文件夹中获取找到
const cocosFileName = fs const cocosFileName = fs.readdirSync('web-mobile').find(file => file.startsWith('cocos2d-js-min') && file.endsWith('.js'));
.readdirSync("web-mobile")
.find(
(file) => file.startsWith("cocos2d-js-min") && file.endsWith(".js")
);
const cocosScriptTag = `<script src="${cocosFileName}" charset="utf-8"></script>\n`; const cocosScriptTag = `<script src="${cocosFileName}" charset="utf-8"></script>\n`;
htmlContent = htmlContent.replace(vConsoleScriptRegex, cocosScriptTag); htmlContent = htmlContent.replace(vConsoleScriptRegex, cocosScriptTag);
@ -135,46 +83,31 @@ async function processHtmlChannels(options) {
const assetMapScriptTag = `<script src="asset-map.js" charset="utf-8"></script>\n`; const assetMapScriptTag = `<script src="asset-map.js" charset="utf-8"></script>\n`;
const bodyScriptTags = `${cocosScriptTag}${loaderScriptTag}${assetMapScriptTag}`; const bodyScriptTags = `${cocosScriptTag}${loaderScriptTag}${assetMapScriptTag}`;
if ( if (!htmlContent.includes(loaderScriptTag) && !htmlContent.includes(assetMapScriptTag)) {
!htmlContent.includes(cocosScriptTag) &&
!htmlContent.includes(assetMapScriptTag)
) {
htmlContent = htmlContent.replace(cocosScriptTag, bodyScriptTags); htmlContent = htmlContent.replace(cocosScriptTag, bodyScriptTags);
} }
// 移除单渠道的 SDK script 标签 // 移除单渠道的 SDK script 标签
if (!["tiktok", "facebook", "google"].includes(channelName)) {
const sdkScriptTag = `<script src="https://sf16-muse-va.ibytedtos.com/obj/union-fe-nc-i18n/playable/sdk/playable-sdk.js"></script>\n`; const sdkScriptTag = `<script src="https://sf16-muse-va.ibytedtos.com/obj/union-fe-nc-i18n/playable/sdk/playable-sdk.js"></script>\n`;
htmlContent = htmlContent.replace(sdkScriptTag, ""); htmlContent = htmlContent.replace(sdkScriptTag, '');
}
fs.writeFileSync(htmlFilePath, htmlContent); fs.writeFileSync(htmlFilePath, htmlContent);
console.log(`已将 ${channelName}.js 和相关 script 引入到 index.html 中`); console.log(`已将 ${channelName}.js 和相关 script 引入到 index.html 中`);
// 调用 do_task 方法,将 web-mobile 下的所有包打包成一个 HTML // 调用 do_task 方法,将 web-mobile 下的所有包打包成一个 HTML
createDist();
do_task(); do_task();
// 如果是 ironsource 渠道,将 <script src="mraid.js"></script> 插入到 body 标签之前 // 如果是 ironsource 渠道,将 <script src="mraid.js"></script> 插入到 body 标签之前
if (channelName === "ironsource") { if (channelName === 'ironsource') {
htmlContent = fs.readFileSync(path.join("dist", "index.html"), "utf-8"); htmlContent = fs.readFileSync(path.join('dist', 'index.html'), 'utf-8');
htmlContent = htmlContent.replace( htmlContent = htmlContent.replace(/<\/body>/, `<script src="mraid.js"></script></body>`);
/<\/body>/, fs.writeFileSync(path.join('dist', 'index.html'), htmlContent);
`<script src="mraid.js"></script></body>` console.log(`已将 <script src="mraid.js"></script> 写入到 ironsource.html 的 body 标签之前`);
);
fs.writeFileSync(path.join("dist", "index.html"), htmlContent);
console.log(
`已将 <script src="mraid.js"></script> 写入到 ironsource.html 的 body 标签之前`
);
} }
// 重命名生成的 index.html 为对应的渠道名称 // 重命名生成的 index.html 为对应的渠道名称
const distIndexPath = path.join("dist", "index.html"); const distIndexPath = path.join('dist', 'index.html');
const channelIndexPath = path.join( const channelIndexPath = path.join('dist', `${outputPrefix}${channelName}.html`);
"dist",
`${outputPrefix}${channelName}.html`
);
if (fs.existsSync(distIndexPath)) { if (fs.existsSync(distIndexPath)) {
fs.renameSync(distIndexPath, channelIndexPath); fs.renameSync(distIndexPath, channelIndexPath);
console.log(`已重命名 index.html 为 ${channelName}.html`); console.log(`已重命名 index.html 为 ${channelName}.html`);
@ -182,4 +115,4 @@ async function processHtmlChannels(options) {
} }
} }
export default processHtmlChannels; processHtmlChannels();

View File

@ -1,6 +1,6 @@
// 填入对应的商店地址 // 填入对应的商店地址
var iosUrl = "https://apps.apple.com/us/app/top-heroes/id6450953550" var iosUrl = "https://apps.apple.com/app/legend-of-mushroom/id6475333787"
var androidUrl = "https://play.google.com/store/apps/details?id=com.greenmushroom.boomblitz.gp&hl=en_US" var androidUrl = "https://play.google.com/store/apps/details?id=com.mxdzzus.google"
class SoyooLifecyle { class SoyooLifecyle {

View File

@ -1,8 +1,9 @@
{ {
"type": "module",
"scripts": { "scripts": {
"build": "node single-html/build.js", "build": "node single-html/build.js",
"build:cicd": "node cicdScript.js" "build:zip": "node zipChannelScript.js",
"build:h5": "node htmlChannelScript.js",
"build:allChannels": "node scriptBefore.js && npm run build:zip && npm run build:h5"
}, },
"devDependencies": { "devDependencies": {
"clean-css": "^5.3.3", "clean-css": "^5.3.3",
@ -10,7 +11,6 @@
}, },
"dependencies": { "dependencies": {
"archiver": "^7.0.1", "archiver": "^7.0.1",
"brotli": "^1.3.3", "brotli": "^1.3.3"
"jsdom": "^26.0.0"
} }
} }

View File

@ -1,19 +1,13 @@
lockfileVersion: '6.0' lockfileVersion: '6.1'
settings: settings:
autoInstallPeers: true autoInstallPeers: true
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
archiver:
specifier: ^7.0.1
version: 7.0.1
brotli: brotli:
specifier: ^1.3.3 specifier: ^1.3.3
version: 1.3.3 version: 1.3.3
jsdom:
specifier: ^26.0.0
version: 26.0.0
devDependencies: devDependencies:
clean-css: clean-css:
@ -25,188 +19,16 @@ devDependencies:
packages: packages:
/@asamuzakjp/css-color@2.8.3:
resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==}
dependencies:
'@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4)(@csstools/css-tokenizer@3.0.3)
'@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4)(@csstools/css-tokenizer@3.0.3)
'@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
'@csstools/css-tokenizer': 3.0.3
lru-cache: 10.4.3
dev: false
/@csstools/color-helpers@5.0.1:
resolution: {integrity: sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==}
engines: {node: '>=18'}
dev: false
/@csstools/css-calc@2.1.1(@csstools/css-parser-algorithms@3.0.4)(@csstools/css-tokenizer@3.0.3):
resolution: {integrity: sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==}
engines: {node: '>=18'}
peerDependencies:
'@csstools/css-parser-algorithms': ^3.0.4
'@csstools/css-tokenizer': ^3.0.3
dependencies:
'@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
'@csstools/css-tokenizer': 3.0.3
dev: false
/@csstools/css-color-parser@3.0.7(@csstools/css-parser-algorithms@3.0.4)(@csstools/css-tokenizer@3.0.3):
resolution: {integrity: sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==}
engines: {node: '>=18'}
peerDependencies:
'@csstools/css-parser-algorithms': ^3.0.4
'@csstools/css-tokenizer': ^3.0.3
dependencies:
'@csstools/color-helpers': 5.0.1
'@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4)(@csstools/css-tokenizer@3.0.3)
'@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
'@csstools/css-tokenizer': 3.0.3
dev: false
/@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3):
resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==}
engines: {node: '>=18'}
peerDependencies:
'@csstools/css-tokenizer': ^3.0.3
dependencies:
'@csstools/css-tokenizer': 3.0.3
dev: false
/@csstools/css-tokenizer@3.0.3:
resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==}
engines: {node: '>=18'}
dev: false
/@isaacs/cliui@8.0.2:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
dependencies:
string-width: 5.1.2
string-width-cjs: /string-width@4.2.3
strip-ansi: 7.1.0
strip-ansi-cjs: /strip-ansi@6.0.1
wrap-ansi: 8.1.0
wrap-ansi-cjs: /wrap-ansi@7.0.0
dev: false
/@pkgjs/parseargs@0.11.0:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
requiresBuild: true
dev: false
optional: true
/abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
dependencies:
event-target-shim: 5.0.1
dev: false
/agent-base@7.1.3:
resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
engines: {node: '>= 14'}
dev: false
/ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
dev: false
/ansi-regex@6.1.0:
resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
engines: {node: '>=12'}
dev: false
/ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
dependencies:
color-convert: 2.0.1
dev: false
/ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
dev: false
/archiver-utils@5.0.2:
resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==}
engines: {node: '>= 14'}
dependencies:
glob: 10.4.5
graceful-fs: 4.2.11
is-stream: 2.0.1
lazystream: 1.0.1
lodash: 4.17.21
normalize-path: 3.0.0
readable-stream: 4.7.0
dev: false
/archiver@7.0.1:
resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==}
engines: {node: '>= 14'}
dependencies:
archiver-utils: 5.0.2
async: 3.2.6
buffer-crc32: 1.0.0
readable-stream: 4.7.0
readdir-glob: 1.1.3
tar-stream: 3.1.7
zip-stream: 6.0.1
dev: false
/async@3.2.6:
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
dev: false
/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: false
/b4a@1.6.7:
resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==}
dev: false
/balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: false
/bare-events@2.5.4:
resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==}
requiresBuild: true
dev: false
optional: true
/base64-js@1.5.1: /base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: false dev: false
/brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
dependencies:
balanced-match: 1.0.2
dev: false
/brotli@1.3.3: /brotli@1.3.3:
resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==}
dependencies: dependencies:
base64-js: 1.5.1 base64-js: 1.5.1
dev: false dev: false
/buffer-crc32@1.0.0:
resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==}
engines: {node: '>=8.0.0'}
dev: false
/buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
dev: false
/clean-css@5.3.3: /clean-css@5.3.3:
resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==}
engines: {node: '>= 10.0'} engines: {node: '>= 10.0'}
@ -214,637 +36,13 @@ packages:
source-map: 0.6.1 source-map: 0.6.1
dev: true dev: true
/color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
dependencies:
color-name: 1.1.4
dev: false
/color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: false
/combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: false
/compress-commons@6.0.2:
resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==}
engines: {node: '>= 14'}
dependencies:
crc-32: 1.2.2
crc32-stream: 6.0.0
is-stream: 2.0.1
normalize-path: 3.0.0
readable-stream: 4.7.0
dev: false
/core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
dev: false
/crc-32@1.2.2:
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
engines: {node: '>=0.8'}
hasBin: true
dev: false
/crc32-stream@6.0.0:
resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==}
engines: {node: '>= 14'}
dependencies:
crc-32: 1.2.2
readable-stream: 4.7.0
dev: false
/cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
dev: false
/cssstyle@4.2.1:
resolution: {integrity: sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==}
engines: {node: '>=18'}
dependencies:
'@asamuzakjp/css-color': 2.8.3
rrweb-cssom: 0.8.0
dev: false
/data-urls@5.0.0:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'}
dependencies:
whatwg-mimetype: 4.0.0
whatwg-url: 14.1.0
dev: false
/debug@4.4.0:
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.3
dev: false
/decimal.js@10.5.0:
resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
dev: false
/delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: false
/eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: false
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: false
/emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
dev: false
/entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
dev: false
/event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
dev: false
/events@3.3.0:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
dev: false
/fast-fifo@1.3.2:
resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
dev: false
/foreground-child@3.3.0:
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
engines: {node: '>=14'}
dependencies:
cross-spawn: 7.0.6
signal-exit: 4.1.0
dev: false
/form-data@4.0.1:
resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false
/glob@10.4.5:
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
hasBin: true
dependencies:
foreground-child: 3.3.0
jackspeak: 3.4.3
minimatch: 9.0.5
minipass: 7.1.2
package-json-from-dist: 1.0.1
path-scurry: 1.11.1
dev: false
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
dev: false
/html-encoding-sniffer@4.0.0:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
dependencies:
whatwg-encoding: 3.1.1
dev: false
/http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.3
debug: 4.4.0
transitivePeerDependencies:
- supports-color
dev: false
/https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.3
debug: 4.4.0
transitivePeerDependencies:
- supports-color
dev: false
/iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
dependencies:
safer-buffer: 2.1.2
dev: false
/ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: false
/inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: false
/is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
dev: false
/is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
dev: false
/is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
dev: false
/isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
dev: false
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: false
/jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
dev: false
/jsdom@26.0.0:
resolution: {integrity: sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==}
engines: {node: '>=18'}
peerDependencies:
canvas: ^3.0.0
peerDependenciesMeta:
canvas:
optional: true
dependencies:
cssstyle: 4.2.1
data-urls: 5.0.0
decimal.js: 10.5.0
form-data: 4.0.1
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.16
parse5: 7.2.1
rrweb-cssom: 0.8.0
saxes: 6.0.0
symbol-tree: 3.2.4
tough-cookie: 5.1.0
w3c-xmlserializer: 5.0.0
webidl-conversions: 7.0.0
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 14.1.0
ws: 8.18.0
xml-name-validator: 5.0.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
dev: false
/lazystream@1.0.1:
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
engines: {node: '>= 0.6.3'}
dependencies:
readable-stream: 2.3.8
dev: false
/lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: false
/lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
dev: false
/mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
dev: false
/mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.52.0
dev: false
/minimatch@5.1.6:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
dependencies:
brace-expansion: 2.0.1
dev: false
/minimatch@9.0.5:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
brace-expansion: 2.0.1
dev: false
/minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
dev: false
/ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
dev: false
/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
dev: false
/nwsapi@2.2.16:
resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==}
dev: false
/package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
dev: false
/parse5@7.2.1:
resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==}
dependencies:
entities: 4.5.0
dev: false
/path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
dev: false
/path-scurry@1.11.1:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
dependencies:
lru-cache: 10.4.3
minipass: 7.1.2
dev: false
/process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: false
/process@0.11.10:
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
engines: {node: '>= 0.6.0'}
dev: false
/punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
dev: false
/queue-tick@1.0.1:
resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
dev: false
/readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
dependencies:
core-util-is: 1.0.3
inherits: 2.0.4
isarray: 1.0.0
process-nextick-args: 2.0.1
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
dev: false
/readable-stream@4.7.0:
resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
abort-controller: 3.0.0
buffer: 6.0.3
events: 3.3.0
process: 0.11.10
string_decoder: 1.3.0
dev: false
/readdir-glob@1.1.3:
resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}
dependencies:
minimatch: 5.1.6
dev: false
/rrweb-cssom@0.8.0:
resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
dev: false
/safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
dev: false
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
dev: false
/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: false
/saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
dependencies:
xmlchars: 2.2.0
dev: false
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
dependencies:
shebang-regex: 3.0.0
dev: false
/shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
dev: false
/signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
dev: false
/source-map@0.6.1: /source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
/streamx@2.21.1:
resolution: {integrity: sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==}
dependencies:
fast-fifo: 1.3.2
queue-tick: 1.0.1
text-decoder: 1.2.3
optionalDependencies:
bare-events: 2.5.4
dev: false
/string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
dev: false
/string-width@5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
dependencies:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.1.0
dev: false
/string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
dependencies:
safe-buffer: 5.1.2
dev: false
/string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
dependencies:
safe-buffer: 5.2.1
dev: false
/strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
dependencies:
ansi-regex: 5.0.1
dev: false
/strip-ansi@7.1.0:
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
engines: {node: '>=12'}
dependencies:
ansi-regex: 6.1.0
dev: false
/symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
dev: false
/tar-stream@3.1.7:
resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
dependencies:
b4a: 1.6.7
fast-fifo: 1.3.2
streamx: 2.21.1
dev: false
/text-decoder@1.2.3:
resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==}
dependencies:
b4a: 1.6.7
dev: false
/tldts-core@6.1.74:
resolution: {integrity: sha512-gTwtY6L2GfuxiL4CWpLknv9JDYYqBvKCk/BT5uAaAvCA0s6pzX7lr2IrkQZSUlnSjRHIjTl8ZwKCVXJ7XNRWYw==}
dev: false
/tldts@6.1.74:
resolution: {integrity: sha512-O5vTZ1UmmEmrLl/59U9igitnSMlprALLaLgbv//dEvjobPT9vyURhHXKMCDLEhn3qxZFIkb9PwAfNYV0Ol7RPQ==}
hasBin: true
dependencies:
tldts-core: 6.1.74
dev: false
/tough-cookie@5.1.0:
resolution: {integrity: sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==}
engines: {node: '>=16'}
dependencies:
tldts: 6.1.74
dev: false
/tr46@5.0.0:
resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
engines: {node: '>=18'}
dependencies:
punycode: 2.3.1
dev: false
/uglify-js@3.19.3: /uglify-js@3.19.3:
resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==}
engines: {node: '>=0.8.0'} engines: {node: '>=0.8.0'}
hasBin: true hasBin: true
dev: true dev: true
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: false
/w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
dependencies:
xml-name-validator: 5.0.0
dev: false
/webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
dev: false
/whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
dependencies:
iconv-lite: 0.6.3
dev: false
/whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'}
dev: false
/whatwg-url@14.1.0:
resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==}
engines: {node: '>=18'}
dependencies:
tr46: 5.0.0
webidl-conversions: 7.0.0
dev: false
/which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
hasBin: true
dependencies:
isexe: 2.0.0
dev: false
/wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
dev: false
/wrap-ansi@8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
dependencies:
ansi-styles: 6.2.1
string-width: 5.1.2
strip-ansi: 7.1.0
dev: false
/ws@8.18.0:
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: false
/xml-name-validator@5.0.0:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
dev: false
/xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
dev: false
/zip-stream@6.0.1:
resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
engines: {node: '>= 14'}
dependencies:
archiver-utils: 5.0.2
compress-commons: 6.0.2
readable-stream: 4.7.0
dev: false

View File

@ -1,22 +1,29 @@
import * as fs from 'fs' const fs = require('fs')
import * as path from 'path' const path = require('path')
import zipChannel from './zipChannelScript.js'
import htmlChannel from './htmlChannelScript.js'
import { fileURLToPath } from 'url'
import { removeDist, createDist } from './common/utils.js'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// 脚本执行之前的操作 // 脚本执行之前的操作
var iosUrl = "https://apps.apple.com/us/app/top-heroes/id6450953550" var iosUrl = "https://apps.apple.com/kr/app/id6467117398"
var androidUrl = "https://play.google.com/store/apps/details?id=com.greenmushroom.boomblitz.gp&hl=en_US" var androidUrl = "https://play.google.com/store/apps/details?id=com.mxdzzus.google"
// 版本一(英文版)
// 安卓https://play.google.com/store/apps/details?id=com.mxdzzus.google
// iOShttps://apps.apple.com/app/legend-of-mushroom/id6475333787
// 版本二(韩语)
// 安卓https://play.google.com/store/apps/details?id=com.mxdzzkr.google
// iOShttps://apps.apple.com/kr/app/id6467117398
removeDist() function removeDist() {
createDist() const isExists = fs.existsSync(path.join(__dirname, 'dist'))
if (isExists) {
fs.rmdirSync(path.join(__dirname, 'dist'), { recursive: true })
}
if (!isExists) {
fs.mkdirSync(path.join(__dirname, 'dist'))
}
}
function replaceAppStoreUrl() { function replaceAppStoreUrl() {
@ -60,35 +67,10 @@ function replaceCss() {
} }
async function startScript() { function startScript() {
removeDist() removeDist()
replaceCss() replaceCss()
replaceAppStoreUrl() replaceAppStoreUrl()
const options = {
// zip包
zipChannel: [
// 'facebook',
// 'google', 'tiktok',
// 'vungle', 'liftoff'
],
// html包
htmlChannel: [
'facebook',
'applovin',
// 'unity',
// 'appier', 'ironsource',
// 'mintegral', 'moloco',
],
outputPrefix: '',
zipToSingleDir: true,
jsPrefix: 'replace_content'
}
await zipChannel(Object.assign(options))
await htmlChannel(Object.assign(options))
console.log('脚本执行完毕') console.log('脚本执行完毕')
} }

View File

@ -1,123 +0,0 @@
import fs from 'fs';
import path from 'path';
import { JSDOM } from 'jsdom';
import archiver from 'archiver'; // 用于打包 ZIP 文件
/**
* 提取 HTML 中的内联 JavaScript 并保存为外部文件
* @param {string} htmlFilePath - HTML 文件路径
* @param {string} outputDir - 输出目录
* @param {string} jsPrefix - JavaScript 文件的前缀名称
*/
export function extractJsFromHtml(htmlFilePath, outputDir, jsPrefix = 'script') {
// 确保输出目录存在
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// 读取 HTML 文件内容
const htmlContent = fs.readFileSync(htmlFilePath, 'utf-8');
// 使用 JSDOM 解析 HTML
const dom = new JSDOM(htmlContent);
const document = dom.window.document;
// 查找所有的 <script> 标签
const scriptTags = document.querySelectorAll('script');
scriptTags.forEach((script, index) => {
// 如果 script 标签有 src 属性,并且是 http 或 https 协议,则跳过
if (script.src && (script.src.startsWith('http://') || script.src.startsWith('https://'))) {
console.log(`跳过外部引用: ${script.src}`);
return;
}
// 如果 script 标签有 src 属性,但不是 http/https则保留
if (script.src) {
return;
}
// 获取内联 JavaScript 代码
const jsCode = script.textContent;
if (!jsCode.trim()) {
return;
}
// 生成外部 JS 文件名
const jsFileName = `${jsPrefix}_${index + 1}.js`;
const jsFilePath = path.join(outputDir, jsFileName);
// 将 JavaScript 代码写入外部文件
fs.writeFileSync(jsFilePath, jsCode, 'utf-8');
// 更新 <script> 标签,改为引用外部文件
script.textContent = ''; // 清空内联代码
script.src = jsFileName; // 添加 src 属性
});
// 保存更新后的 HTML 文件为 index.html
const updatedHtmlContent = dom.serialize();
const updatedHtmlFilePath = path.join(outputDir, 'index.html');
fs.writeFileSync(updatedHtmlFilePath, updatedHtmlContent, 'utf-8');
console.log(`拆分完成!更新后的 HTML 文件已保存为: ${updatedHtmlFilePath}`);
}
/**
* 将目录打包为 ZIP 文件
* @param {string} sourceDir - 要打包的目录
* @param {string} outputZipFile - 输出的 ZIP 文件路径
*/
function zipDirectory(sourceDir, outputZipFile) {
const output = fs.createWriteStream(outputZipFile);
const archive = archiver('zip', { zlib: { level: 9 } }); // 设置压缩级别
// 监听打包完成事件
output.on('close', () => {
console.log(`打包完成ZIP 文件已保存为: ${outputZipFile}`);
});
// 监听错误事件
archive.on('error', (err) => {
throw err;
});
// 将输出流与 archiver 关联
archive.pipe(output);
// 将目录内容添加到 ZIP 文件中
archive.directory(sourceDir, false);
// 完成打包
archive.finalize();
}
/**
* 处理 dist 目录下的所有 HTML 文件
* @param {string} distDir - dist 目录路径
* @param {string} jsPrefix - JavaScript 文件的前缀名称
*/
export function processHtmlFiles(distDir, jsPrefix = 'script') {
// 获取 dist 目录下的所有文件
const files = fs.readdirSync(distDir);
files.forEach((file) => {
const filePath = path.join(distDir, file);
// 只处理 HTML 文件
if (path.extname(file).toLowerCase() === '.html') {
const outputDir = path.join(distDir, path.basename(file, '.html')); // 输出目录
const zipFilePath = path.join(distDir, `${path.basename(file, '.html')}.zip`); // ZIP 文件路径
// 提取 JS 并生成 index.html
extractJsFromHtml(filePath, outputDir, jsPrefix);
// 将输出目录打包为 ZIP 文件
zipDirectory(outputDir, zipFilePath);
// 清理临时输出目录(可选)
fs.rmSync(outputDir, { recursive: true, force: true });
console.log(`清理临时目录: ${outputDir}`);
}
});
}

View File

@ -1,7 +1,8 @@
// Provides functions for encoding/decoding data to and from base-122. // Provides functions for encoding/decoding data to and from base-122.
import * as fs from 'fs' let fs = require('fs')
import readline from 'readline' , readline = require('readline')
;
const kString = 0 const kString = 0
, kUint8Array = 1 , kUint8Array = 1
@ -247,11 +248,10 @@ function print8Bits(num) {
return "00000000".substring(num.toString(2).length) + num.toString(2); return "00000000".substring(num.toString(2).length) + num.toString(2);
} }
module.exports = {
export { encode: encode,
encode, decode: decode,
decode, encodeFromBase64: encodeFromBase64,
encodeFromBase64, encodeFile: encodeFile,
encodeFile, utf8DataToString: utf8DataToString
utf8DataToString
}; };

View File

@ -1,10 +1,9 @@
// 全部改成es6写法 const fs = require("fs")
import * as fs from 'fs' const path = require("path")
import * as path from 'path' const uglify = require("uglify-js")
import uglify from 'uglify-js' const CleanCSS = require("clean-css")
import CleanCSS from 'clean-css' const brotli = require('brotli');
import brotli from 'brotli' const base122 = require("./base122")
import * as base122 from './base122.js'
/** /**
* - [注意] 路径问题.start脚本与web-mobile同层级,因此相对路径需要带上web-mobile;cocos在调用资源时没有web-mobile,需要在最后去掉 * - [注意] 路径问题.start脚本与web-mobile同层级,因此相对路径需要带上web-mobile;cocos在调用资源时没有web-mobile,需要在最后去掉
@ -101,11 +100,7 @@ function replaceRelativeScript(html) {
matchedScripts.forEach((script) => { matchedScripts.forEach((script) => {
var reg = /(\<script src\=")(.+\.js)(".*>)/g; var reg = /(\<script src\=")(.+\.js)(".*>)/g;
var jsPath = reg.exec(script)[2]; var jsPath = reg.exec(script)[2];
// 不是远程访问的
console.log("写入", jsPath) console.log("写入", jsPath)
// if (jsPath.startsWith("https://") || jsPath.startsWith("http://")) {
// return
// }
html = html.replace(script, () => get_html_code_by_js_file(path.resolve(C.BASE_PATH, jsPath))) html = html.replace(script, () => get_html_code_by_js_file(path.resolve(C.BASE_PATH, jsPath)))
}); });
@ -134,4 +129,5 @@ function do_task() {
console.timeEnd("输出html文件") console.timeEnd("输出html文件")
} }
do_task() // 导出
module.exports = do_task

View File

@ -1,26 +1,24 @@
import * as fs from "fs"; const fs = require("fs");
import * as path from "path"; const path = require("path");
import archiver from "archiver"; const archiver = require("archiver");
import { createDist } from "./common/utils.js";
import do_task from "./single-html/build.js";
async function processChannels(options) { const zipChannel = ['facebook', 'google', 'tiktok', 'vungle', 'liftoff'];
const zipChannel = options.zipChannel || [
"facebook",
"google",
"tiktok",
"vungle",
"liftoff",
];
const outputPrefix = options.outputPrefix || "";
const zipToSingleDir = options.zipToSingleDir || false;
const outputPrefix = '';
// 创建 dist 目录
if (!fs.existsSync('dist')) {
fs.mkdirSync('dist');
}
// 1. 将js复制到web-mobile中
async function processChannels() {
for (const channelName of zipChannel) { for (const channelName of zipChannel) {
// 遍历 networks 目录下的所有js文件,获取 文件名 然后删除 web-mobile 目录下的同名文件 // 遍历 networks 目录下的所有js文件,获取 文件名 然后删除 web-mobile 目录下的同名文件
const files = fs.readdirSync(path.join("networks")); const files = fs.readdirSync(path.join('networks'));
for (const file of files) { for (const file of files) {
if (file.endsWith(".js")) { if (file.endsWith('.js')) {
const destinationFile = path.join("web-mobile", file); const destinationFile = path.join('web-mobile', file);
if (fs.existsSync(destinationFile)) { if (fs.existsSync(destinationFile)) {
fs.unlinkSync(destinationFile); fs.unlinkSync(destinationFile);
console.log(`已删除: ${destinationFile}`); console.log(`已删除: ${destinationFile}`);
@ -28,32 +26,26 @@ async function processChannels(options) {
} }
} }
// tiktok 处理 // tiktok 处理
const tiktokConfig = path.join("web-mobile", `config.json`); const tiktokConfig = path.join('web-mobile', `config.json`);
if (fs.existsSync(tiktokConfig)) { if (fs.existsSync(tiktokConfig)) {
fs.unlinkSync(tiktokConfig); fs.unlinkSync(tiktokConfig);
} }
const tiktokJs = path.join("web-mobile", `tiktok.js`); const tiktokJs = path.join('web-mobile', `tiktok.js`);
if (fs.existsSync(tiktokJs)) { if (fs.existsSync(tiktokJs)) {
fs.unlinkSync(tiktokJs); fs.unlinkSync(tiktokJs);
} }
// 从 networks 找到复制到 web-mobile 中 // 从 networks 找到复制到 web-mobile 中
const sourceFile = path.join("networks", `${channelName}.js`); const sourceFile = path.join('networks', `${channelName}.js`);
const destinationFile = path.join("web-mobile", `${channelName}.js`); const destinationFile = path.join('web-mobile', `${channelName}.js`);
if (channelName === "tiktok") { if (channelName === 'tiktok') {
fs.copyFileSync( fs.copyFileSync(path.join('tiktok/config.json'), path.join('web-mobile', `config.json`));
path.join("tiktok/config.json"), fs.copyFileSync(path.join('tiktok/tiktok.js'), path.join('web-mobile', `tiktok.js`));
path.join("web-mobile", `config.json`) } else if (channelName === 'vungle' || channelName === 'liftoff') {
);
fs.copyFileSync(
path.join("tiktok/tiktok.js"),
path.join("web-mobile", `tiktok.js`)
);
} else if (channelName === "vungle" || channelName === "liftoff") {
// 对于 vungle 和 liftoff使用 mraid_support.js // 对于 vungle 和 liftoff使用 mraid_support.js
const mraidSupportFile = path.join("networks", "mraid_support.js"); const mraidSupportFile = path.join('networks', 'mraid_support.js');
const mraidDestinationFile = path.join("web-mobile", "mraid_support.js"); const mraidDestinationFile = path.join('web-mobile', 'mraid_support.js');
if (fs.existsSync(mraidSupportFile)) { if (fs.existsSync(mraidSupportFile)) {
fs.copyFileSync(mraidSupportFile, mraidDestinationFile); fs.copyFileSync(mraidSupportFile, mraidDestinationFile);
console.log(`已复制: ${mraidSupportFile}${mraidDestinationFile}`); console.log(`已复制: ${mraidSupportFile}${mraidDestinationFile}`);
@ -65,114 +57,66 @@ async function processChannels(options) {
} }
} }
const htmlFilePath = path.join("web-mobile", "index.html"); const htmlFilePath = path.join('web-mobile', 'index.html');
let htmlContent = fs.readFileSync(htmlFilePath, "utf-8"); let htmlContent = fs.readFileSync(htmlFilePath, 'utf-8');
// 移除其他渠道包的 <script> 标签 // 移除其他渠道包的 <script> 标签
zipChannel.forEach((channel) => { zipChannel.forEach(channel => {
const scriptRegex = new RegExp( const scriptRegex = new RegExp(`<script src="${channel}.js" charset="utf-8"></script>\\n`, 'g');
`<script src="${channel}.js" charset="utf-8"></script>\\n`, htmlContent = htmlContent.replace(scriptRegex, '');
"g"
);
htmlContent = htmlContent.replace(scriptRegex, "");
}); });
// 移除所有渠道的 <meta> 标签 // 移除所有渠道的 <meta> 标签
const metaTagRegex = const metaTagRegex = /<meta name="ad.size" content="width=480,height=320">\n/g;
/<meta name="ad.size" content="width=480,height=320">\n/g; htmlContent = htmlContent.replace(metaTagRegex, '');
htmlContent = htmlContent.replace(metaTagRegex, "");
// 检查并插入新的渠道 script 标签 // 检查并插入新的渠道 script 标签
let channelScriptTag; let channelScriptTag;
if (channelName === "vungle" || channelName === "liftoff") { if (channelName === 'vungle' || channelName === 'liftoff') {
channelScriptTag = ` <script src="mraid_support.js" charset="utf-8"></script>\n`; channelScriptTag = ` <script src="mraid_support.js" charset="utf-8"></script>\n`;
} else { } else {
channelScriptTag = ` <script src="${channelName}.js" charset="utf-8"></script>\n`; channelScriptTag = ` <script src="${channelName}.js" charset="utf-8"></script>\n`;
} }
if (!htmlContent.includes(channelScriptTag)) { if (!htmlContent.includes(channelScriptTag)) {
htmlContent = htmlContent.replace( htmlContent = htmlContent.replace(/<\/head>/, `${channelScriptTag}</head>`);
/<\/head>/,
`${channelScriptTag}</head>`
);
} }
// 如果是 google 渠道,添加 <meta> 标签 // 如果是 google 渠道,添加 <meta> 标签
if (channelName === "google") { if (channelName === 'google') {
const metaTag = `<meta name="ad.size" content="width=480,height=320">\n`; const metaTag = `<meta name="ad.size" content="width=480,height=320">\n`;
if (!htmlContent.includes(metaTag)) { if (!htmlContent.includes(metaTag)) {
htmlContent = htmlContent.replace(/<\/head>/, `${metaTag}</head>`); htmlContent = htmlContent.replace(/<\/head>/, `${metaTag}</head>`);
} }
} }
// 检查并插入新的 SDK script 标签到 <body> 之前
const sdkScriptTag = `<script src="https://sf16-muse-va.ibytedtos.com/obj/union-fe-nc-i18n/playable/sdk/playable-sdk.js"></script>\n`;
if (!htmlContent.includes(sdkScriptTag)) {
htmlContent = htmlContent.replace(/<\/body>/, `${sdkScriptTag}</body>`);
}
fs.writeFileSync(htmlFilePath, htmlContent); fs.writeFileSync(htmlFilePath, htmlContent);
console.log(`已将 ${channelName}.js 和 SDK script 引入到 index.html 中`); console.log(`已将 ${channelName}.js 和 SDK script 引入到 index.html 中`);
createDist();
if (!zipToSingleDir) {
// 打包 web-mobile 目录为 zip 文件 // 打包 web-mobile 目录为 zip 文件
const output = fs.createWriteStream( const output = fs.createWriteStream(path.join('dist', `${outputPrefix}${channelName}.zip`));
path.join("dist", `${outputPrefix}${channelName}.zip`) const archive = archiver('zip', {
); zlib: { level: 9 } // 设置压缩级别
const archive = archiver("zip", {
zlib: { level: 9 }, // 设置压缩级别
}); });
output.on("close", function () { output.on('close', function () {
console.log( console.log(`已创建 ${channelName}.zip大小为 ${archive.pointer()} 字节`);
`已创建 ${channelName}.zip大小为 ${archive.pointer()} 字节`
);
}); });
archive.on("error", function (err) { archive.on('error', function (err) {
throw err; throw err;
}); });
archive.pipe(output); archive.pipe(output);
archive.directory("web-mobile/", false); archive.directory('web-mobile/', false);
await archive.finalize(); await archive.finalize();
} else {
// 单html包
const htmlFilePath = path.join("web-mobile", "index.html");
let htmlContent = fs.readFileSync(htmlFilePath, "utf-8");
// 替换包含 window.vConsole = new VConsole 的 <script> 标签
const vConsoleScriptRegex =
/<script type="text\/javascript">[\s\S]*?window\.vConsole = new VConsole\(\);[\s\S]*?<\/script>/;
// @TODO cocos的名称需要从web-mobile文件夹中获取找到
const cocosFileName = fs
.readdirSync("web-mobile")
.find(
(file) => file.startsWith("cocos2d-js-min") && file.endsWith(".js")
);
const cocosScriptTag = `<script src="${cocosFileName}" charset="utf-8"></script>\n`;
htmlContent = htmlContent.replace(vConsoleScriptRegex, cocosScriptTag);
// 额外引入 loader-and-starter.js 和 asset-map.js 到 body 中
const loaderScriptTag = `<script src="loader-and-starter.js" charset="utf-8"></script>\n`;
const assetMapScriptTag = `<script src="asset-map.js" charset="utf-8"></script>\n`;
const bodyScriptTags = `${cocosScriptTag}${loaderScriptTag}${assetMapScriptTag}`;
if (
!htmlContent.includes(loaderScriptTag) &&
!htmlContent.includes(assetMapScriptTag)
) {
htmlContent = htmlContent.replace(cocosScriptTag, bodyScriptTags);
}
// 检查并插入新的 SDK script 标签到 <body> 之前
// const sdkScriptTag = `<script src="https://sf16-muse-va.ibytedtos.com/obj/union-fe-nc-i18n/playable/sdk/playable-sdk.js"></script>\n`;
// if (!htmlContent.includes(sdkScriptTag)) {
// htmlContent = htmlContent.replace(/<\/body>/, `${sdkScriptTag}</body>`);
// }
fs.writeFileSync(htmlFilePath, htmlContent);
console.log(`已将 ${channelName}.js 和相关 script 引入到 index.html 中`);
do_task();
}
} }
} }
export default processChannels; processChannels();