382 lines
11 KiB
Go
382 lines
11 KiB
Go
package unpack
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/Ackites/KillWxapkg/utils"
|
|
|
|
"github.com/dop251/goja"
|
|
"golang.org/x/net/html"
|
|
)
|
|
|
|
// GwxCfg 结构体定义
|
|
type GwxCfg struct{}
|
|
|
|
// GWX 空方法
|
|
func (g *GwxCfg) GWX() {}
|
|
|
|
// CSS 重建函数
|
|
func cssRebuild(pureData map[string]interface{}, importCnt map[string]int, actualPure map[string]string, blockCss []string, cssFile string, result map[string]string, commonStyle map[string]interface{}, onlyTest *bool) func(data interface{}) {
|
|
// 统计导入的 CSS 文件
|
|
var statistic func(data interface{})
|
|
statistic = func(data interface{}) {
|
|
addStat := func(id string) {
|
|
if _, exists := importCnt[id]; !exists {
|
|
importCnt[id] = 1
|
|
statistic(pureData[id])
|
|
} else {
|
|
importCnt[id]++
|
|
}
|
|
}
|
|
|
|
switch v := data.(type) {
|
|
case float64:
|
|
addStat(fmt.Sprintf("%v", v))
|
|
case string:
|
|
addStat(v)
|
|
case []interface{}:
|
|
for _, content := range v {
|
|
if contentArr, ok := content.([]interface{}); ok && int(contentArr[0].(float64)) == 2 {
|
|
addStat(fmt.Sprintf("%v", contentArr[1]))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 生成 CSS 样式
|
|
var makeup func(data interface{}) string
|
|
makeup = func(data interface{}) string {
|
|
log.Printf("Processing data: %v\n", data)
|
|
isPure := false
|
|
switch data.(type) {
|
|
case string:
|
|
isPure = true
|
|
}
|
|
if *onlyTest {
|
|
statistic(data)
|
|
if !isPure {
|
|
dataArr := data.([]interface{})
|
|
if len(dataArr) == 1 && int(dataArr[0].([]interface{})[0].(float64)) == 2 {
|
|
data = dataArr[0].([]interface{})[1]
|
|
} else {
|
|
return ""
|
|
}
|
|
}
|
|
if actualPure[data.(string)] == "" && !contains(blockCss, changeExt(toDir2(cssFile, ""), "")) {
|
|
actualPure[data.(string)] = cssFile
|
|
}
|
|
return ""
|
|
}
|
|
|
|
var res strings.Builder
|
|
attach := ""
|
|
if isPure && actualPure[data.(string)] != cssFile {
|
|
if actualPure[data.(string)] != "" {
|
|
return fmt.Sprintf("@import \"%s.wxss\";\n", toDir2(actualPure[data.(string)], cssFile))
|
|
}
|
|
res.WriteString(fmt.Sprintf("/*! Import by _C[%s], whose real path we cannot found. */", data.(string)))
|
|
attach = "/*! Import end */"
|
|
}
|
|
|
|
exactData := data
|
|
if isPure {
|
|
exactData = pureData[data.(string)]
|
|
}
|
|
|
|
if styleData, ok := commonStyle[data.(string)]; ok {
|
|
if styleArray, ok := styleData.([]interface{}); ok {
|
|
var fileStyle strings.Builder
|
|
for _, content := range styleArray {
|
|
if contentStr, ok := content.(string); ok && contentStr != "1" {
|
|
fileStyle.WriteString(contentStr)
|
|
} else if contentArr, ok := content.([]interface{}); ok && len(contentArr) != 1 {
|
|
fileStyle.WriteString(fmt.Sprintf("%vrpx", contentArr[1]))
|
|
}
|
|
}
|
|
return fileStyle.String()
|
|
}
|
|
} else {
|
|
if dataArr, ok := exactData.([]interface{}); ok {
|
|
for _, content := range dataArr {
|
|
if contentArr, ok := content.([]interface{}); ok {
|
|
switch int(contentArr[0].(float64)) {
|
|
case 0: // rpx
|
|
res.WriteString(fmt.Sprintf("%vrpx", contentArr[1]))
|
|
case 1: // add suffix, ignore it for restoring correct!
|
|
case 2: // import
|
|
res.WriteString(makeup(contentArr[1]))
|
|
}
|
|
} else {
|
|
res.WriteString(content.(string))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res.String() + attach
|
|
}
|
|
|
|
// 返回处理函数
|
|
return func(data interface{}) {
|
|
log.Printf("Processing CSS file: %s\n", cssFile)
|
|
if result[cssFile] == "" {
|
|
result[cssFile] = ""
|
|
}
|
|
result[cssFile] += makeup(data)
|
|
}
|
|
}
|
|
|
|
// 运行 JavaScript 代码
|
|
func runVM(name string, code string, pureData map[string]interface{}, importCnt map[string]int, actualPure map[string]string, blockCss []string, result map[string]string, commonStyle map[string]interface{}, onlyTest *bool) {
|
|
vm := goja.New()
|
|
wxAppCode := make(map[string]func())
|
|
|
|
// 添加 console.log
|
|
err := vm.Set("console", map[string]interface{}{
|
|
"log": func(msg string) {
|
|
log.Printf(msg)
|
|
},
|
|
})
|
|
if err != nil {
|
|
log.Printf("Error setting console log: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// 设置 setCssToHead 函数
|
|
err = vm.Set("setCssToHead", cssRebuild(pureData, importCnt, actualPure, blockCss, name, result, commonStyle, onlyTest))
|
|
if err != nil {
|
|
log.Printf("Error setting setCssToHead: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// 设置 __wxAppCode__
|
|
err = vm.Set("__wxAppCode__", wxAppCode)
|
|
if err != nil {
|
|
log.Printf("Error setting __wxAppCode__: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// 运行 JavaScript 代码
|
|
_, err = vm.RunString(code)
|
|
if err != nil {
|
|
log.Printf("Error running JavaScript code: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// 调用 wxAppCode 中的函数
|
|
log.Printf("Running wxAppCode...\n")
|
|
for _, fn := range wxAppCode {
|
|
fn()
|
|
}
|
|
}
|
|
|
|
// ProcessXssFiles 处理 WXSS 文件
|
|
func ProcessXssFiles(dir string, mainDir string) {
|
|
saveDir := dir
|
|
isSubPkg := mainDir != ""
|
|
if isSubPkg {
|
|
saveDir = mainDir
|
|
}
|
|
|
|
var runList = make(map[string]string)
|
|
var pureData = make(map[string]interface{})
|
|
var result = make(map[string]string)
|
|
var actualPure = make(map[string]string)
|
|
var importCnt = make(map[string]int)
|
|
var blockCss []string // 自定义阻止导入的 CSS 文件(无扩展名)
|
|
var commonStyle map[string]interface{}
|
|
var onlyTest = true
|
|
|
|
// 预运行,读取所有相关文件
|
|
preRun := func(dir, frameFile, mainCode string, files []string, cb func()) {
|
|
runList[filepath.Join(dir, "./app.wxss")] = mainCode
|
|
|
|
for _, name := range files {
|
|
if name != frameFile {
|
|
code, err := os.ReadFile(name)
|
|
if err != nil {
|
|
log.Printf("Error reading file: %v\n", err)
|
|
continue
|
|
}
|
|
codeStr := string(code)
|
|
codeStr = strings.Replace(codeStr, "display:-webkit-box;display:-webkit-flex;", "", -1)
|
|
codeStr = codeStr[:strings.Index(codeStr, "\n")] // 确保只取第一行
|
|
if strings.Contains(codeStr, "setCssToHead(") {
|
|
runList[name] = codeStr[strings.Index(codeStr, "setCssToHead("):]
|
|
}
|
|
}
|
|
}
|
|
|
|
cb()
|
|
}
|
|
|
|
// 一次性运行所有 JavaScript 代码
|
|
runOnce := func() {
|
|
for name, code := range runList {
|
|
runVM(name, code, pureData, importCnt, actualPure, blockCss, result, commonStyle, &onlyTest)
|
|
}
|
|
}
|
|
|
|
// 扫描目录中的所有 HTML 文件
|
|
scanDirByExtTo(dir, ".html", func(files []string) {
|
|
var frameFile string
|
|
if _, err := os.Stat(filepath.Join(dir, "page-frame.html")); err == nil {
|
|
frameFile = filepath.Join(dir, "page-frame.html")
|
|
} else if _, err := os.Stat(filepath.Join(dir, "app-wxss.js")); err == nil {
|
|
frameFile = filepath.Join(dir, "app-wxss.js")
|
|
} else if _, err := os.Stat(filepath.Join(dir, "page-frame.js")); err == nil {
|
|
frameFile = filepath.Join(dir, "page-frame.js")
|
|
} else {
|
|
log.Printf("未找到类似 page-frame 的文件")
|
|
return
|
|
}
|
|
|
|
code, err := os.ReadFile(frameFile)
|
|
if err != nil {
|
|
log.Printf("Error reading file: %v\n", err)
|
|
return
|
|
}
|
|
|
|
codeStr := string(code)
|
|
codeStr = strings.Replace(codeStr, "display:-webkit-box;display:-webkit-flex;", "", -1)
|
|
scriptCode := codeStr
|
|
|
|
if strings.HasSuffix(frameFile, ".html") {
|
|
doc, err := html.Parse(strings.NewReader(codeStr))
|
|
if err != nil {
|
|
log.Printf("Error parsing HTML: %v\n", err)
|
|
return
|
|
}
|
|
var scriptBuilder strings.Builder
|
|
var f func(*html.Node)
|
|
f = func(n *html.Node) {
|
|
if n.Type == html.ElementNode && n.Data == "script" {
|
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
if c.Type == html.TextNode {
|
|
scriptBuilder.WriteString(c.Data)
|
|
}
|
|
}
|
|
}
|
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
f(c)
|
|
}
|
|
}
|
|
f(doc)
|
|
scriptCode = scriptBuilder.String()
|
|
}
|
|
|
|
window := map[string]interface{}{
|
|
"screen": map[string]interface{}{
|
|
"width": 720,
|
|
"height": 1028,
|
|
"orientation": map[string]interface{}{
|
|
"type": "vertical",
|
|
},
|
|
},
|
|
}
|
|
navigator := map[string]interface{}{
|
|
"userAgent": "iPhone",
|
|
}
|
|
|
|
scriptCode = scriptCode[strings.LastIndex(scriptCode, "window.__wcc_version__"):]
|
|
mainCode := fmt.Sprintf(`window=%s;navigator=%s;var __mainPageFrameReady__=window.__mainPageFrameReady__ || function() {};var __WXML_GLOBAL__={entrys:{},defines:{},modules:{},ops:[],wxs_nf_init:undefined,total_ops:0};var __vd_version_info__=__vd_version_info__ || {};%s`,
|
|
toJSON(window), toJSON(navigator), scriptCode)
|
|
|
|
// 处理 commonStyles
|
|
if idx := strings.Index(codeStr, "__COMMON_STYLESHEETS__ || {}"); idx != -1 {
|
|
start := idx + 28
|
|
end := strings.Index(codeStr[start:], "var setCssToHead = function(file, _xcInvalid, info)")
|
|
if end != -1 {
|
|
commonStyles := codeStr[start : start+end]
|
|
vm := goja.New()
|
|
_, err := vm.RunString(fmt.Sprintf(";var __COMMON_STYLESHEETS__ = __COMMON_STYLESHEETS__ || {};%s;__COMMON_STYLESHEETS__;", commonStyles))
|
|
if err == nil {
|
|
err = vm.ExportTo(vm.Get("__COMMON_STYLESHEETS__"), &commonStyle)
|
|
if err != nil {
|
|
log.Printf("Error exporting common styles: %v\n", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mainCode = strings.Replace(mainCode, "var setCssToHead = function", "var setCssToHead2 = function", 1)
|
|
codeStr = codeStr[strings.LastIndex(codeStr, "var setCssToHead = function(file, _xcInvalid"):]
|
|
codeStr = strings.Replace(codeStr, "__COMMON_STYLESHEETS__", "[]", 1)
|
|
if idx := strings.Index(codeStr, "_C = "); idx != -1 {
|
|
codeStr = codeStr[strings.LastIndex(codeStr, "var _C = "):]
|
|
} else {
|
|
codeStr = codeStr[strings.LastIndex(codeStr, "var _C= "):]
|
|
}
|
|
|
|
codeStr = codeStr[:strings.Index(codeStr, "\n")]
|
|
|
|
vm := goja.New()
|
|
_, err = vm.RunString(codeStr + "\n_C")
|
|
if err != nil {
|
|
log.Printf("Error running JavaScript code: %v\n", err)
|
|
return
|
|
}
|
|
err = vm.ExportTo(vm.Get("_C"), &pureData)
|
|
if err != nil {
|
|
log.Printf("Error exporting pure data: %v\n", err)
|
|
return
|
|
}
|
|
|
|
preRun(dir, frameFile, mainCode, files, func() {
|
|
runOnce()
|
|
onlyTest = false
|
|
runOnce()
|
|
|
|
for name, content := range result {
|
|
name = filepath.Join(saveDir, changeExt(name, ".wxss"))
|
|
err := os.WriteFile(name, []byte(utils.TransformCSS(content)), 0755)
|
|
log.Printf("Save wxss %s done.\n", name)
|
|
if err != nil {
|
|
log.Printf("Error saving file: %v\n", err)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// toJSON 将对象转换为 JSON 字符串
|
|
func toJSON(obj interface{}) string {
|
|
data, _ := json.Marshal(obj)
|
|
return string(data)
|
|
}
|
|
|
|
// scanDirByExtTo 扫描目录中的文件并返回指定扩展名的文件列表
|
|
func scanDirByExtTo(dir, ext string, cb func([]string)) {
|
|
var files []string
|
|
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
if !info.IsDir() && strings.HasSuffix(info.Name(), ext) {
|
|
files = append(files, path)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
cb(files)
|
|
}
|
|
|
|
// contains 检查切片是否包含特定元素
|
|
func contains(slice []string, item string) bool {
|
|
for _, v := range slice {
|
|
if v == item {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// toDir2 生成目录路径
|
|
func toDir2(filePath, frameName string) string {
|
|
dir := filepath.Dir(filePath)
|
|
return filepath.Join(dir, frameName)
|
|
}
|