soyoo-cocos/single-html/build.js

134 lines
4.7 KiB
JavaScript
Raw Normal View History

2024-12-05 21:14:41 +08:00
const fs = require("fs")
const path = require("path")
const uglify = require("uglify-js")
const CleanCSS = require("clean-css")
2024-12-06 18:05:40 +08:00
const brotli = require('brotli');
const base122 = require("./base122")
2024-12-05 21:14:41 +08:00
/**
* - [注意] 路径问题.start脚本与web-mobile同层级,因此相对路径需要带上web-mobile;cocos在调用资源时没有web-mobile,需要在最后去掉
*/
const C = {
BASE_PATH: "web-mobile", // web-mobile包基础路径
RES_PATH: "web-mobile/assets", // web-mobile包下的res路径
RES_BASE64_EXTNAME_SET: new Set([ // 需要使用base64编码的资源后缀(根据项目自行扩充)
".png", ".jpg", ".webp", ".mp3",
]),
INPUT_LOADER_STARTER_JS: "single-html/loader-and-starter.js",
OUTPUT_RES_JS: "./web-mobile/asset-map.js", // 输出文件asset-map.js
OUTPUT_LOADER_STARTER_JS: "./web-mobile/loader-and-starter.js",
OUTPUT_INDEX_HTML: "dist/index.html", // 输出文件index.html的路径
INPUT_HTML_FILE: "./web-mobile/index.html",
}
/**
* 读取文件内容
* - 特定后缀返回base64编码后字符串,否则直接返回文件内容字符串
* @param filepath
*/
function get_file_content(filepath) {
let file = fs.readFileSync(filepath)
return C.RES_BASE64_EXTNAME_SET.has(path.extname(filepath)) ? file.toString("base64") : file.toString()
}
/**
* 获取路径下的所有子文件路径(深度遍历)
* @param filepath
*/
function get_all_child_file(filepath) {
let children = [filepath]
for (; ;) {
// 如果都是file类型的,则跳出循环
if (children.every(v => fs.statSync(v).isFile())) { break }
// 如果至少有1个directroy类型,则删除这一项,并加入其子项
children.forEach((child, i) => {
if (fs.statSync(child).isDirectory()) {
delete children[i]
let child_children = fs.readdirSync(child).map(v => `${child}/${v}`)
children.push(...child_children)
}
})
}
return children
}
/**
* 将所有res路径下的资源转化为res.js
* - 存储方式为:res-url(注意是相对的),res文件内容字符串或编码
*/
function write_resjs() {
// 读取并写入到一个对象中
let res_object = {}
get_all_child_file(C.RES_PATH).forEach(path => {
// 注意,存储时删除BASE_PATH前置
let store_path = path.replace(new RegExp(`^${C.BASE_PATH}/`), "")
res_object[store_path] = get_file_content(path)
})
// 写入文件
2024-12-06 18:05:40 +08:00
var compressedAssetsJs = Buffer.from(base122.encode(brotli.compress(new TextEncoder().encode(`window.assetMap=${JSON.stringify(res_object)}`))), 'utf-8')
fs.writeFileSync(C.OUTPUT_RES_JS, `eval(new TextDecoder().decode(window.unbrotli(base122ToArrayBuffer("${compressedAssetsJs}"))))`)
2024-12-05 21:14:41 +08:00
}
/** 将js文件转化为html文件内容(包括压缩过程) */
function get_html_code_by_js_file(js_filepath) {
let js = get_file_content(js_filepath)
let min_js = uglify.minify(js).code
return `<script type="text/javascript" charset="utf-8">${min_js}`
}
/** 将css文件转化为html文件内容(包括压缩过程) */
function get_html_code_by_css_file(css_filepath) {
let css = get_file_content(css_filepath)
let min_css = new CleanCSS().minify(css).styles
return `<style>${min_css}</style>`
}
function replaceRelativeCss(html) {
var reg = /\<link rel\="stylesheet" type\="text\/css" href\="(.+)"\/\>/g
var matchedCsss = html.match(reg);
matchedCsss.forEach((script) => {
var CssPath = reg.exec(script)[1];
html = html.replace(script, () => get_html_code_by_css_file(path.resolve(C.BASE_PATH, CssPath)))
});
return html;
}
function replaceRelativeScript(html) {
var matchedScripts = html.match(/\<script src\="(.+)"\>/g);
matchedScripts.forEach((script) => {
var reg = /(\<script src\=")(.+\.js)(".*>)/g;
var jsPath = reg.exec(script)[2];
2024-12-06 18:05:40 +08:00
console.log("写入", jsPath)
2024-12-05 21:14:41 +08:00
html = html.replace(script, () => get_html_code_by_js_file(path.resolve(C.BASE_PATH, jsPath)))
});
return html;
}
/** 执行任务 */
function do_task() {
// 前置:将res资源写成res.js
console.time("写入res")
write_resjs()
console.timeEnd("写入res")
fs.writeFileSync(C.OUTPUT_LOADER_STARTER_JS, get_file_content(C.INPUT_LOADER_STARTER_JS))
let html = get_file_content(C.INPUT_HTML_FILE)
console.time("替换js")
html = replaceRelativeScript(html)
html = replaceRelativeCss(html)
console.timeEnd("替换js")
// 写入文件并提示成功
console.time("输出html文件")
fs.writeFileSync(C.OUTPUT_INDEX_HTML, html)
console.timeEnd("输出html文件")
}
// 导出
module.exports = do_task