storm-lua-minify 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +20 -0
- package/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/ast2lua.js +669 -0
- package/dist/cli.js +68 -0
- package/dist/index.js +43 -0
- package/package.json +27 -0
- package/src/ast2lua.ts +799 -0
- package/src/cli.ts +82 -0
- package/tsconfig.json +109 -0
package/dist/ast2lua.js
ADDED
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// based on "luamin": Copyright Mathias Bynens <https://mathiasbynens.be/>
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.Minifier = void 0;
|
|
6
|
+
const source_map_1 = require("source-map");
|
|
7
|
+
const PRECEDENCE = {
|
|
8
|
+
or: 1,
|
|
9
|
+
and: 2,
|
|
10
|
+
"<": 3,
|
|
11
|
+
">": 3,
|
|
12
|
+
"<=": 3,
|
|
13
|
+
">=": 3,
|
|
14
|
+
"~=": 3,
|
|
15
|
+
"==": 3,
|
|
16
|
+
"..": 5,
|
|
17
|
+
"+": 6,
|
|
18
|
+
"-": 6, // binary -
|
|
19
|
+
"*": 7,
|
|
20
|
+
"/": 7,
|
|
21
|
+
"%": 7,
|
|
22
|
+
unarynot: 8,
|
|
23
|
+
"unary#": 8,
|
|
24
|
+
"unary-": 8, // unary -
|
|
25
|
+
"^": 10,
|
|
26
|
+
};
|
|
27
|
+
const IDENTIFIER_PARTS = [
|
|
28
|
+
"0",
|
|
29
|
+
"1",
|
|
30
|
+
"2",
|
|
31
|
+
"3",
|
|
32
|
+
"4",
|
|
33
|
+
"5",
|
|
34
|
+
"6",
|
|
35
|
+
"7",
|
|
36
|
+
"8",
|
|
37
|
+
"9",
|
|
38
|
+
"a",
|
|
39
|
+
"b",
|
|
40
|
+
"c",
|
|
41
|
+
"d",
|
|
42
|
+
"e",
|
|
43
|
+
"f",
|
|
44
|
+
"g",
|
|
45
|
+
"h",
|
|
46
|
+
"i",
|
|
47
|
+
"j",
|
|
48
|
+
"k",
|
|
49
|
+
"l",
|
|
50
|
+
"m",
|
|
51
|
+
"n",
|
|
52
|
+
"o",
|
|
53
|
+
"p",
|
|
54
|
+
"q",
|
|
55
|
+
"r",
|
|
56
|
+
"s",
|
|
57
|
+
"t",
|
|
58
|
+
"u",
|
|
59
|
+
"v",
|
|
60
|
+
"w",
|
|
61
|
+
"x",
|
|
62
|
+
"y",
|
|
63
|
+
"z",
|
|
64
|
+
"A",
|
|
65
|
+
"B",
|
|
66
|
+
"C",
|
|
67
|
+
"D",
|
|
68
|
+
"E",
|
|
69
|
+
"F",
|
|
70
|
+
"G",
|
|
71
|
+
"H",
|
|
72
|
+
"I",
|
|
73
|
+
"J",
|
|
74
|
+
"K",
|
|
75
|
+
"L",
|
|
76
|
+
"M",
|
|
77
|
+
"N",
|
|
78
|
+
"O",
|
|
79
|
+
"P",
|
|
80
|
+
"Q",
|
|
81
|
+
"R",
|
|
82
|
+
"S",
|
|
83
|
+
"T",
|
|
84
|
+
"U",
|
|
85
|
+
"V",
|
|
86
|
+
"W",
|
|
87
|
+
"X",
|
|
88
|
+
"Y",
|
|
89
|
+
"Z",
|
|
90
|
+
"_",
|
|
91
|
+
];
|
|
92
|
+
const identifierMap = new Map();
|
|
93
|
+
const identifiersInUse = new Set();
|
|
94
|
+
function wrapArray(obj) {
|
|
95
|
+
if (Array.isArray(obj)) {
|
|
96
|
+
return obj;
|
|
97
|
+
}
|
|
98
|
+
return [obj];
|
|
99
|
+
}
|
|
100
|
+
function generateZeroes(length) {
|
|
101
|
+
let zero = "0";
|
|
102
|
+
let result = "";
|
|
103
|
+
if (length < 1) {
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
if (length == 1) {
|
|
107
|
+
return zero;
|
|
108
|
+
}
|
|
109
|
+
while (length) {
|
|
110
|
+
if (length & 1) {
|
|
111
|
+
result += zero;
|
|
112
|
+
}
|
|
113
|
+
// eslint-disable-next-line no-cond-assign
|
|
114
|
+
if ((length >>= 1)) {
|
|
115
|
+
zero += zero;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
function isKeyword(id) {
|
|
121
|
+
switch (id.length) {
|
|
122
|
+
case 2:
|
|
123
|
+
return "do" == id || "if" == id || "in" == id || "or" == id;
|
|
124
|
+
case 3:
|
|
125
|
+
return ("and" == id || "end" == id || "for" == id || "nil" == id || "not" == id);
|
|
126
|
+
case 4:
|
|
127
|
+
return "else" == id || "goto" == id || "then" == id || "true" == id;
|
|
128
|
+
case 5:
|
|
129
|
+
return ("break" == id ||
|
|
130
|
+
"false" == id ||
|
|
131
|
+
"local" == id ||
|
|
132
|
+
"until" == id ||
|
|
133
|
+
"while" == id);
|
|
134
|
+
case 6:
|
|
135
|
+
return "elseif" == id || "repeat" == id || "return" == id;
|
|
136
|
+
case 8:
|
|
137
|
+
return "function" == id;
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
function isNeedSeparator(a, b) {
|
|
142
|
+
const lastCharA = a.slice(-1);
|
|
143
|
+
const firstCharB = b.charAt(0);
|
|
144
|
+
const regexAlphaUnderscore = /[a-zA-Z_]/;
|
|
145
|
+
const regexAlphaNumUnderscore = /[a-zA-Z0-9_]/;
|
|
146
|
+
const regexDigits = /[0-9]/;
|
|
147
|
+
if (lastCharA == "" || firstCharB == "") {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (regexAlphaUnderscore.test(lastCharA)) {
|
|
151
|
+
if (regexAlphaNumUnderscore.test(firstCharB)) {
|
|
152
|
+
// e.g. `while` + `1`
|
|
153
|
+
// e.g. `local a` + `local b`
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// e.g. `not` + `(2>3 or 3<2)`
|
|
158
|
+
// e.g. `x` + `^`
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (regexDigits.test(lastCharA)) {
|
|
163
|
+
if (firstCharB == "(" ||
|
|
164
|
+
!(firstCharB == "." || regexAlphaUnderscore.test(firstCharB))) {
|
|
165
|
+
// e.g. `1` + `+`
|
|
166
|
+
// e.g. `1` + `==`
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
// e.g. `1` + `..`
|
|
171
|
+
// e.g. `1` + `and`
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (lastCharA == firstCharB && lastCharA == "-") {
|
|
176
|
+
// e.g. `1-` + `-2`
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
const secondLastCharA = a.slice(-2, -1);
|
|
180
|
+
if (lastCharA == "." &&
|
|
181
|
+
secondLastCharA != "." &&
|
|
182
|
+
regexAlphaNumUnderscore.test(firstCharB)) {
|
|
183
|
+
// e.g. `1.` + `print`
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
function addWithSeparator(val, adding, separator = " ") {
|
|
189
|
+
if (isNeedSeparator(val.toString(), wrapArray(adding)
|
|
190
|
+
.map((p) => p.toString())
|
|
191
|
+
.join())) {
|
|
192
|
+
val.add(separator);
|
|
193
|
+
}
|
|
194
|
+
val.add(adding);
|
|
195
|
+
return val;
|
|
196
|
+
}
|
|
197
|
+
function prependWithSeparator(val, prepending, separator = " ") {
|
|
198
|
+
if (isNeedSeparator(wrapArray(prepending)
|
|
199
|
+
.map((p) => p.toString())
|
|
200
|
+
.join(), val.toString())) {
|
|
201
|
+
val.prepend(separator);
|
|
202
|
+
}
|
|
203
|
+
val.prepend(prepending);
|
|
204
|
+
return val;
|
|
205
|
+
}
|
|
206
|
+
function insertSeparator(a, b, separator = " ") {
|
|
207
|
+
return isNeedSeparator(a.toString(), b.toString()) ? separator : undefined;
|
|
208
|
+
}
|
|
209
|
+
class Minifier {
|
|
210
|
+
fileName;
|
|
211
|
+
ast;
|
|
212
|
+
requireHelper;
|
|
213
|
+
constructor(fileName, ast, requireHelper) {
|
|
214
|
+
this.fileName = fileName;
|
|
215
|
+
this.ast = ast;
|
|
216
|
+
this.requireHelper = requireHelper;
|
|
217
|
+
ast.globals?.map((v) => identifiersInUse.add(v.name));
|
|
218
|
+
}
|
|
219
|
+
parse() {
|
|
220
|
+
const body = this.formatStatementList(this.ast.body);
|
|
221
|
+
/*if (this.ast.comments) {
|
|
222
|
+
const comments = this.ast.comments as Comment[];
|
|
223
|
+
comments.reverse().filter(v => v.raw.includes("--#") || v.raw.includes("[[#")).forEach(comment => {
|
|
224
|
+
body.prepend(this.sourceNodeHelper(comment, comment.raw));
|
|
225
|
+
})
|
|
226
|
+
return body;
|
|
227
|
+
} else*/ {
|
|
228
|
+
return body;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
sourceNodeHelper(node, chuncks, name) {
|
|
232
|
+
const line = node?.loc?.start.line;
|
|
233
|
+
const column = node?.loc?.start.column;
|
|
234
|
+
return new source_map_1.SourceNode(line == undefined ? null : line, column == undefined ? null : column, this.fileName, // 本当に自分のファイル名でよいかは要検討
|
|
235
|
+
chuncks, name);
|
|
236
|
+
}
|
|
237
|
+
formatStatementList(body) {
|
|
238
|
+
const result = this.sourceNodeHelper(undefined, []);
|
|
239
|
+
wrapArray(body).forEach((statement) => {
|
|
240
|
+
addWithSeparator(result, this.formatStatement(statement), "\n");
|
|
241
|
+
});
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
formatStatement(statement) {
|
|
245
|
+
if (statement.type == "AssignmentStatement") {
|
|
246
|
+
// left-hand side
|
|
247
|
+
const variables = statement.variables
|
|
248
|
+
.map((variable) => [this.formatExpression(variable), ","])
|
|
249
|
+
.flat();
|
|
250
|
+
const inits = statement.init
|
|
251
|
+
.map((init) => [this.formatExpression(init), ","])
|
|
252
|
+
.flat();
|
|
253
|
+
const result = this.sourceNodeHelper(statement, this.sourceNodeHelper(undefined, variables.slice(0, -1)));
|
|
254
|
+
addWithSeparator(result, "=");
|
|
255
|
+
addWithSeparator(result, this.sourceNodeHelper(undefined, inits.slice(0, -1)));
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
else if (statement.type == "LocalStatement") {
|
|
259
|
+
const variables = statement.variables
|
|
260
|
+
.map((variable) => [this.formatExpression(variable), ","])
|
|
261
|
+
.flat();
|
|
262
|
+
const result = this.sourceNodeHelper(statement, [
|
|
263
|
+
"local ",
|
|
264
|
+
this.sourceNodeHelper(undefined, variables.slice(0, -1)),
|
|
265
|
+
]);
|
|
266
|
+
if (statement.init.length) {
|
|
267
|
+
const inits = statement.init
|
|
268
|
+
.map((init) => [this.formatExpression(init), ","])
|
|
269
|
+
.flat();
|
|
270
|
+
addWithSeparator(result, "=");
|
|
271
|
+
addWithSeparator(result, this.sourceNodeHelper(undefined, inits.slice(0, -1)));
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
else if (statement.type == "CallStatement") {
|
|
276
|
+
return this.formatExpression(statement.expression); // NOTE: もう一度囲んでもいい
|
|
277
|
+
}
|
|
278
|
+
else if (statement.type == "IfStatement") {
|
|
279
|
+
const result = this.sourceNodeHelper(statement, []);
|
|
280
|
+
statement.clauses.forEach((clause) => {
|
|
281
|
+
const clauseMap = this.sourceNodeHelper(clause, []);
|
|
282
|
+
if (clause.type == "IfClause") {
|
|
283
|
+
addWithSeparator(clauseMap, "if");
|
|
284
|
+
addWithSeparator(clauseMap, this.formatExpression(clause.condition));
|
|
285
|
+
addWithSeparator(clauseMap, "then");
|
|
286
|
+
}
|
|
287
|
+
else if (clause.type == "ElseifClause") {
|
|
288
|
+
addWithSeparator(clauseMap, "elseif");
|
|
289
|
+
addWithSeparator(clauseMap, this.formatExpression(clause.condition));
|
|
290
|
+
addWithSeparator(clauseMap, "then");
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
addWithSeparator(clauseMap, "else");
|
|
294
|
+
}
|
|
295
|
+
addWithSeparator(clauseMap, this.formatStatementList(clause.body));
|
|
296
|
+
addWithSeparator(result, clauseMap);
|
|
297
|
+
});
|
|
298
|
+
addWithSeparator(result, "end");
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
else if (statement.type == "WhileStatement") {
|
|
302
|
+
const result = this.sourceNodeHelper(statement, "while");
|
|
303
|
+
addWithSeparator(result, this.formatExpression(statement.condition));
|
|
304
|
+
addWithSeparator(result, "do");
|
|
305
|
+
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
306
|
+
addWithSeparator(result, "end");
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
else if (statement.type == "DoStatement") {
|
|
310
|
+
const result = this.sourceNodeHelper(statement, "do");
|
|
311
|
+
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
312
|
+
addWithSeparator(result, "end");
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
else if (statement.type == "ReturnStatement") {
|
|
316
|
+
const result = this.sourceNodeHelper(statement, "return");
|
|
317
|
+
if (statement.arguments.length) {
|
|
318
|
+
const returns = statement.arguments
|
|
319
|
+
.map((argument) => [this.formatExpression(argument), ","])
|
|
320
|
+
.flat();
|
|
321
|
+
addWithSeparator(result, returns.slice(0, -1));
|
|
322
|
+
}
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
else if (statement.type == "BreakStatement") {
|
|
326
|
+
return this.sourceNodeHelper(statement, "break");
|
|
327
|
+
}
|
|
328
|
+
else if (statement.type == "RepeatStatement") {
|
|
329
|
+
const result = this.sourceNodeHelper(statement, "repeat");
|
|
330
|
+
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
331
|
+
addWithSeparator(result, "until");
|
|
332
|
+
addWithSeparator(result, this.formatExpression(statement.condition));
|
|
333
|
+
return result;
|
|
334
|
+
}
|
|
335
|
+
else if (statement.type == "FunctionDeclaration") {
|
|
336
|
+
const result = this.sourceNodeHelper(statement, (statement.isLocal ? "local " : "") + "function ");
|
|
337
|
+
if (statement.identifier) {
|
|
338
|
+
addWithSeparator(result, this.formatExpression(statement.identifier));
|
|
339
|
+
}
|
|
340
|
+
addWithSeparator(result, "(");
|
|
341
|
+
if (statement.parameters.length) {
|
|
342
|
+
const parameters = statement.parameters
|
|
343
|
+
.map((parameter) => {
|
|
344
|
+
return [
|
|
345
|
+
parameter.type == "Identifier"
|
|
346
|
+
? this.generateIdentifier(parameter)
|
|
347
|
+
: parameter.value,
|
|
348
|
+
",",
|
|
349
|
+
];
|
|
350
|
+
})
|
|
351
|
+
.flat();
|
|
352
|
+
addWithSeparator(result, parameters.slice(0, -1));
|
|
353
|
+
}
|
|
354
|
+
addWithSeparator(result, ")");
|
|
355
|
+
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
356
|
+
addWithSeparator(result, "end");
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
else if (statement.type == "ForGenericStatement") {
|
|
360
|
+
// see also `ForNumericStatement`
|
|
361
|
+
const result = this.sourceNodeHelper(statement, "for");
|
|
362
|
+
const variables = statement.variables
|
|
363
|
+
.map((variable) => [this.generateIdentifier(variable), ","])
|
|
364
|
+
.flat();
|
|
365
|
+
const iterators = statement.iterators
|
|
366
|
+
.map((iterator) => [this.formatExpression(iterator), ","])
|
|
367
|
+
.flat();
|
|
368
|
+
addWithSeparator(result, variables.slice(0, -1));
|
|
369
|
+
addWithSeparator(result, "in");
|
|
370
|
+
addWithSeparator(result, iterators.slice(0, -1));
|
|
371
|
+
addWithSeparator(result, "do");
|
|
372
|
+
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
373
|
+
addWithSeparator(result, "end");
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
else if (statement.type == "ForNumericStatement") {
|
|
377
|
+
// The variables in a `ForNumericStatement` are always local
|
|
378
|
+
const result = this.sourceNodeHelper(statement, "for");
|
|
379
|
+
addWithSeparator(result, this.generateIdentifier(statement.variable));
|
|
380
|
+
addWithSeparator(result, "=");
|
|
381
|
+
addWithSeparator(result, this.formatExpression(statement.start));
|
|
382
|
+
addWithSeparator(result, ",");
|
|
383
|
+
addWithSeparator(result, this.formatExpression(statement.end));
|
|
384
|
+
if (statement.step) {
|
|
385
|
+
addWithSeparator(result, ",");
|
|
386
|
+
addWithSeparator(result, this.formatExpression(statement.step));
|
|
387
|
+
}
|
|
388
|
+
addWithSeparator(result, "do");
|
|
389
|
+
addWithSeparator(result, this.formatStatementList(statement.body));
|
|
390
|
+
addWithSeparator(result, "end");
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
else if (statement.type == "LabelStatement") {
|
|
394
|
+
// The identifier names in a `LabelStatement` can safely be renamed
|
|
395
|
+
return this.sourceNodeHelper(statement, [
|
|
396
|
+
"::",
|
|
397
|
+
this.generateIdentifier(statement.label),
|
|
398
|
+
"::",
|
|
399
|
+
]);
|
|
400
|
+
}
|
|
401
|
+
else if (statement.type == "GotoStatement") {
|
|
402
|
+
// The identifier names in a `GotoStatement` can safely be renamed
|
|
403
|
+
return this.sourceNodeHelper(statement, [
|
|
404
|
+
"goto ",
|
|
405
|
+
this.generateIdentifier(statement.label),
|
|
406
|
+
]);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
throw TypeError("Unknown statement type: `" + JSON.stringify(statement) + "`");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/*function joinStatements(a: string | SourceNode, b: string | SourceNode, separator = " ") {
|
|
413
|
+
return isNeedSeparator(a.toString(), b.toString()) ? a.toString() + separator + b.toString() : a.toString() + b.toString();
|
|
414
|
+
}*/
|
|
415
|
+
formatExpression(expression, argOptions) {
|
|
416
|
+
if (expression.type == "Identifier") {
|
|
417
|
+
return this.sourceNodeHelper(expression,
|
|
418
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error
|
|
419
|
+
//@ts-ignore
|
|
420
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
421
|
+
expression.isLocal
|
|
422
|
+
? this.generateIdentifier(expression, true)
|
|
423
|
+
: expression.name, expression.name);
|
|
424
|
+
}
|
|
425
|
+
else if (expression.type == "StringLiteral" ||
|
|
426
|
+
expression.type == "NumericLiteral" ||
|
|
427
|
+
expression.type == "BooleanLiteral" ||
|
|
428
|
+
expression.type == "NilLiteral" ||
|
|
429
|
+
expression.type == "VarargLiteral") {
|
|
430
|
+
return this.sourceNodeHelper(expression, expression.raw);
|
|
431
|
+
}
|
|
432
|
+
else if (expression.type == "LogicalExpression" ||
|
|
433
|
+
expression.type == "BinaryExpression") {
|
|
434
|
+
const operator = expression.operator;
|
|
435
|
+
const currentPrecedence = PRECEDENCE[operator];
|
|
436
|
+
let associativity = "left";
|
|
437
|
+
const options = {
|
|
438
|
+
precedence: 0,
|
|
439
|
+
preserveIdentifiers: false,
|
|
440
|
+
...argOptions,
|
|
441
|
+
};
|
|
442
|
+
const leftHand = this.formatExpression(expression.left, {
|
|
443
|
+
precedence: currentPrecedence,
|
|
444
|
+
direction: "left",
|
|
445
|
+
parent: operator,
|
|
446
|
+
});
|
|
447
|
+
const rightHand = this.formatExpression(expression.right, {
|
|
448
|
+
precedence: currentPrecedence,
|
|
449
|
+
direction: "right",
|
|
450
|
+
parent: operator,
|
|
451
|
+
});
|
|
452
|
+
if (operator == "^" || operator == "..") {
|
|
453
|
+
associativity = "right";
|
|
454
|
+
}
|
|
455
|
+
else if (currentPrecedence < options.precedence ||
|
|
456
|
+
(currentPrecedence == options.precedence &&
|
|
457
|
+
associativity != options.direction &&
|
|
458
|
+
options.parent != "+" &&
|
|
459
|
+
!(options.parent == "*" && (operator == "/" || operator == "*")))) {
|
|
460
|
+
return this.sourceNodeHelper(expression, [
|
|
461
|
+
"(",
|
|
462
|
+
leftHand,
|
|
463
|
+
insertSeparator(leftHand, operator),
|
|
464
|
+
operator,
|
|
465
|
+
insertSeparator(operator, rightHand),
|
|
466
|
+
rightHand,
|
|
467
|
+
")",
|
|
468
|
+
].filter((p) => p !== undefined));
|
|
469
|
+
}
|
|
470
|
+
return this.sourceNodeHelper(expression, [
|
|
471
|
+
leftHand,
|
|
472
|
+
insertSeparator(leftHand, operator),
|
|
473
|
+
operator,
|
|
474
|
+
insertSeparator(operator, rightHand),
|
|
475
|
+
rightHand,
|
|
476
|
+
].filter((p) => p !== undefined));
|
|
477
|
+
}
|
|
478
|
+
else if (expression.type == "UnaryExpression") {
|
|
479
|
+
const operator = expression.operator;
|
|
480
|
+
const currentPrecedence = PRECEDENCE["unary" + operator];
|
|
481
|
+
const options = {
|
|
482
|
+
precedence: 0,
|
|
483
|
+
...argOptions,
|
|
484
|
+
};
|
|
485
|
+
const p2 = this.formatExpression(expression.argument, {
|
|
486
|
+
precedence: currentPrecedence,
|
|
487
|
+
});
|
|
488
|
+
const result = this.sourceNodeHelper(expression, [operator, insertSeparator(operator, p2), p2].filter((p) => p !== undefined));
|
|
489
|
+
if (currentPrecedence < options.precedence &&
|
|
490
|
+
// In principle, we should parenthesize the RHS of an
|
|
491
|
+
// expression like `3^-2`, because `^` has higher precedence
|
|
492
|
+
// than unary `-` according to the manual. But that is
|
|
493
|
+
// misleading on the RHS of `^`, since the parser will
|
|
494
|
+
// always try to find a unary operator regardless of
|
|
495
|
+
// precedence.
|
|
496
|
+
!(options.parent == "^" && options.direction == "right")) {
|
|
497
|
+
result.prepend("(");
|
|
498
|
+
result.add(")");
|
|
499
|
+
}
|
|
500
|
+
return result;
|
|
501
|
+
}
|
|
502
|
+
else if (expression.type == "CallExpression") {
|
|
503
|
+
// requireの展開モードは2種類: SLモード-その場に読み込み, FLモード-require相当の関数で呼び出し
|
|
504
|
+
if (this.formatBase(expression.base).toString() == "require" &&
|
|
505
|
+
expression?.arguments[0].type === "StringLiteral") {
|
|
506
|
+
const res = this.requireHelper(expression.arguments[0].raw.replaceAll('"', ""));
|
|
507
|
+
if (res != undefined) {
|
|
508
|
+
return res;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const args = expression.arguments
|
|
512
|
+
.map((arg) => [this.formatExpression(arg), ","])
|
|
513
|
+
.flat();
|
|
514
|
+
return this.sourceNodeHelper(expression, [
|
|
515
|
+
this.formatBase(expression.base),
|
|
516
|
+
"(",
|
|
517
|
+
this.sourceNodeHelper(undefined, args.slice(0, -1)),
|
|
518
|
+
")",
|
|
519
|
+
]);
|
|
520
|
+
}
|
|
521
|
+
else if (expression.type == "TableCallExpression") {
|
|
522
|
+
return this.sourceNodeHelper(expression, [
|
|
523
|
+
this.formatExpression(expression.base),
|
|
524
|
+
this.formatExpression(expression.arguments),
|
|
525
|
+
]);
|
|
526
|
+
}
|
|
527
|
+
else if (expression.type == "StringCallExpression") {
|
|
528
|
+
return this.sourceNodeHelper(expression, [
|
|
529
|
+
this.formatExpression(expression.base),
|
|
530
|
+
this.formatExpression(expression.argument),
|
|
531
|
+
]);
|
|
532
|
+
}
|
|
533
|
+
else if (expression.type == "IndexExpression") {
|
|
534
|
+
return this.sourceNodeHelper(expression, [
|
|
535
|
+
this.formatBase(expression.base),
|
|
536
|
+
"[",
|
|
537
|
+
this.formatExpression(expression.index),
|
|
538
|
+
"]",
|
|
539
|
+
]);
|
|
540
|
+
}
|
|
541
|
+
else if (expression.type == "MemberExpression") {
|
|
542
|
+
return this.sourceNodeHelper(expression, [
|
|
543
|
+
this.formatBase(expression.base),
|
|
544
|
+
expression.indexer,
|
|
545
|
+
this.formatExpression(expression.identifier, {
|
|
546
|
+
preserveIdentifiers: true,
|
|
547
|
+
}),
|
|
548
|
+
]);
|
|
549
|
+
}
|
|
550
|
+
else if (expression.type == "FunctionDeclaration") {
|
|
551
|
+
const result = this.sourceNodeHelper(expression, ["function", "("]);
|
|
552
|
+
if (expression.parameters.length) {
|
|
553
|
+
const parameters = expression.parameters
|
|
554
|
+
.map((parameter) => {
|
|
555
|
+
return [
|
|
556
|
+
this.sourceNodeHelper(parameter, parameter.type === "Identifier"
|
|
557
|
+
? this.generateIdentifier(parameter)
|
|
558
|
+
: parameter.value),
|
|
559
|
+
",",
|
|
560
|
+
];
|
|
561
|
+
})
|
|
562
|
+
.flat();
|
|
563
|
+
addWithSeparator(result, parameters.slice(0, -1));
|
|
564
|
+
}
|
|
565
|
+
result.add(")");
|
|
566
|
+
const body = this.formatStatementList(expression.body);
|
|
567
|
+
addWithSeparator(result, body);
|
|
568
|
+
addWithSeparator(result, "end");
|
|
569
|
+
return result;
|
|
570
|
+
}
|
|
571
|
+
else if (expression.type == "TableConstructorExpression") {
|
|
572
|
+
const result = this.sourceNodeHelper(expression, "{");
|
|
573
|
+
const fields = expression.fields
|
|
574
|
+
.map((field, ix, ar) => {
|
|
575
|
+
// Stormworks "propert" Trailing Comma: https://nona-takahara.github.io/blog/entry11.html
|
|
576
|
+
const comma = ix !== ar.length - 1 ||
|
|
577
|
+
this.formatExpression(field.value).toString().includes("property")
|
|
578
|
+
? ","
|
|
579
|
+
: undefined;
|
|
580
|
+
if (field.type == "TableKey") {
|
|
581
|
+
return this.sourceNodeHelper(field, [
|
|
582
|
+
this.sourceNodeHelper(undefined, [
|
|
583
|
+
"[",
|
|
584
|
+
this.formatExpression(field.key),
|
|
585
|
+
"]",
|
|
586
|
+
]),
|
|
587
|
+
"=",
|
|
588
|
+
this.formatExpression(field.value),
|
|
589
|
+
comma,
|
|
590
|
+
].filter((p) => p !== undefined));
|
|
591
|
+
}
|
|
592
|
+
else if (field.type == "TableValue") {
|
|
593
|
+
return [this.formatExpression(field.value), comma].filter((p) => p !== undefined);
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
// at this point, `field.type == 'TableKeyString'`
|
|
597
|
+
// TODO: keep track of nested scopes (#18)
|
|
598
|
+
return this.sourceNodeHelper(field, [
|
|
599
|
+
this.formatExpression(field.key, { preserveIdentifiers: true }),
|
|
600
|
+
"=",
|
|
601
|
+
this.formatExpression(field.value),
|
|
602
|
+
comma,
|
|
603
|
+
].filter((p) => p !== undefined));
|
|
604
|
+
}
|
|
605
|
+
})
|
|
606
|
+
.flat();
|
|
607
|
+
addWithSeparator(result, fields);
|
|
608
|
+
addWithSeparator(result, "}");
|
|
609
|
+
return result;
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
throw TypeError("Unknown expression type: `" + JSON.stringify(expression) + "`");
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
formatBase(base) {
|
|
616
|
+
const type = base.type;
|
|
617
|
+
const needsParens = type == "CallExpression" ||
|
|
618
|
+
type == "BinaryExpression" ||
|
|
619
|
+
type == "FunctionDeclaration" ||
|
|
620
|
+
type == "TableConstructorExpression" ||
|
|
621
|
+
type == "LogicalExpression" ||
|
|
622
|
+
type == "StringLiteral";
|
|
623
|
+
const result = this.sourceNodeHelper(base, this.formatExpression(base));
|
|
624
|
+
if (needsParens) {
|
|
625
|
+
prependWithSeparator(result, "(");
|
|
626
|
+
addWithSeparator(result, ")");
|
|
627
|
+
}
|
|
628
|
+
return result;
|
|
629
|
+
}
|
|
630
|
+
currentIdentifier = "";
|
|
631
|
+
generateIdentifier(nameItem, nested = false) {
|
|
632
|
+
if (nameItem.name === "self") {
|
|
633
|
+
return this.sourceNodeHelper(nameItem, "self", "self");
|
|
634
|
+
}
|
|
635
|
+
const defined = identifierMap.get(nameItem.name);
|
|
636
|
+
if (defined) {
|
|
637
|
+
return this.sourceNodeHelper(nameItem, defined, nameItem.name); // 第3引数は要調査
|
|
638
|
+
}
|
|
639
|
+
const length = this.currentIdentifier.length;
|
|
640
|
+
let position = length - 1;
|
|
641
|
+
let character;
|
|
642
|
+
let index;
|
|
643
|
+
while (position >= 0) {
|
|
644
|
+
character = this.currentIdentifier.charAt(position);
|
|
645
|
+
index = IDENTIFIER_PARTS.indexOf(character);
|
|
646
|
+
if (index != IDENTIFIER_PARTS.length - 1) {
|
|
647
|
+
this.currentIdentifier =
|
|
648
|
+
this.currentIdentifier.substring(0, position) +
|
|
649
|
+
IDENTIFIER_PARTS[index + 1] +
|
|
650
|
+
generateZeroes(length - (position + 1));
|
|
651
|
+
if (isKeyword(this.currentIdentifier) ||
|
|
652
|
+
identifiersInUse.has(this.currentIdentifier)) {
|
|
653
|
+
return this.generateIdentifier(nameItem, nested);
|
|
654
|
+
}
|
|
655
|
+
identifierMap.set(nameItem.name, this.currentIdentifier);
|
|
656
|
+
return this.generateIdentifier(nameItem, nested);
|
|
657
|
+
}
|
|
658
|
+
--position;
|
|
659
|
+
}
|
|
660
|
+
this.currentIdentifier = "a" + generateZeroes(length);
|
|
661
|
+
if (identifiersInUse.has(this.currentIdentifier)) {
|
|
662
|
+
return this.generateIdentifier(nameItem, nested);
|
|
663
|
+
}
|
|
664
|
+
identifierMap.set(nameItem.name, this.currentIdentifier);
|
|
665
|
+
return this.generateIdentifier(nameItem, nested);
|
|
666
|
+
// return this.sourceNodeHelper(nameItem, nameItem.name, nested ? nameItem.name : undefined);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
exports.Minifier = Minifier;
|