forked from yatyricky/lua-bundler
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.js
191 lines (182 loc) · 5.1 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
const fs = require("fs")
const path = require("path")
const readline = require("readline")
const luamin = require("luamin")
const logger = require("./logger")
const codeHeader = `
local __modules = {}
local require = function(path)
local module = __modules[path]
if module ~= nil then
if not module.inited then
module.cached = module.loader()
module.inited = true
end
return module.cached
else
error("module not found")
return nil
end
end
`
const startMark = "--nef-inject"
const endMark = "--nef-inject-end"
const regex = new RegExp(/^(?!--).*require\(["']([a-zA-Z0-9\/]+)["']\).*$/gm)
let workDir = "."
/**
* @type {{[key: string] : boolean}}
*/
const requireList = {}
let outStr
/**
* @param {fs.PathLike} mainPath
*/
function recurseFiles(name) {
if (requireList[name]) {
return
}
const fpath = path.join(workDir, name + ".lua")
if (!fs.existsSync(fpath) || !fs.statSync(fpath).isFile()) {
logger.error(`File not found ${fpath}`)
return
}
const newNames = []
const file = fs.readFileSync(fpath).toString()
outStr += `\n----------------\n`
outStr += `__modules["${name}"] = { inited = false, cached = false, loader = function(...)`
outStr += `\n---- START ${name}.lua ----\n`
outStr += file
outStr += `\n---- END ${name}.lua ----\n`
outStr += ` end}`
requireList[name] = true
// requires
let match
do {
match = regex.exec(file)
if (match !== null) {
if (requireList[match[1]] === undefined) {
requireList[match[1]] = false
newNames.push(match[1])
}
}
} while (match !== null)
for (let i = 0; i < newNames.length; i++) {
recurseFiles(newNames[i])
}
}
function findUnusedFiles(dir, list, dirbase) {
if (dirbase === undefined) {
dirbase = ""
} else {
dirbase = dirbase + "/"
}
const files = fs.readdirSync(dir)
for (const file of files) {
const full = path.join(dir, file)
const st = fs.statSync(full)
if (st.isFile()) {
if (file.endsWith(".lua")) {
const base = path.basename(full, ".lua")
const key = dirbase + base
if (requireList[key] === undefined) {
list.push(key)
}
}
} else if (st.isDirectory()) {
findUnusedFiles(full, list, dirbase + path.basename(file))
} else {
logger.error("WTF is " + full)
}
}
}
/**
* @param {fs.PathLike} mainPath
* @returns {string}
*/
function emitCode(mainPath) {
if (!fs.existsSync(mainPath) || !fs.statSync(mainPath).isFile()) {
logger.error(`File not found ${mainPath}`)
return ""
}
workDir = path.dirname(mainPath)
outStr = codeHeader
const mainName = path.basename(mainPath, ".lua")
recurseFiles(mainName)
const unused = []
findUnusedFiles(workDir, unused)
for (const file of unused) {
logger.warn("Unused file " + file + ".lua")
}
return outStr + `\n__modules["${mainName}"].loader()`
}
function injectWC3(mainPath, wc3path, mode) {
if (!fs.existsSync(wc3path) || !fs.statSync(wc3path).isFile()) {
logger.error(`File not found ${wc3path}`)
return
}
let file = emitCode(mainPath)
if (mode === "-p") {
file = luamin.minify(file)
}
file = file.replace(/'(....)'/gm, (a, b) => {
const v1 = b.charCodeAt(0) << 24
const v2 = b.charCodeAt(1) << 16
const v3 = b.charCodeAt(2) << 8
const v4 = b.charCodeAt(3)
return "(" + (v1 | v2 | v3 | v4) + ")"
})
const ri = readline.createInterface({
input: fs.createReadStream(wc3path),
})
let state = 0
let outFile = ""
let changed = false
ri.on("line", function (line) {
if (line.trim() === endMark) {
outFile += file + "\n"
state = 0
}
if (state === 2) {
return
}
if (state === 0 && line === "function main()") {
state = 1
changed = true
}
if (state === 1 && line === "end") {
outFile += startMark + "\n"
outFile += file + "\n"
outFile += endMark + "\n"
state = 0
}
if (line.trim() === startMark) {
state = 2
changed = true
}
outFile += line + "\n"
})
ri.on("close", function () {
fs.writeFileSync(wc3path, outFile)
if (changed) {
logger.success("Write to war3map.lua success")
} else {
logger.error("Target file is not war3map.lua")
}
})
}
function toFile(mainPath, outPath, mode) {
if (fs.existsSync(outPath) && fs.statSync(outPath).isDirectory()) {
logger.error(`Target is dir ${outPath}`)
return
}
let file = emitCode(mainPath)
if (mode === "-p") {
file = luamin.minify(file)
}
fs.writeFileSync(outPath, file)
logger.success(`Write to ${outPath} success`)
}
module.exports = {
injectWC3,
toFile,
}