nextlua 2.0.0 → 3.0.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/package.json +1 -1
- package/src/main.js +273 -24
package/package.json
CHANGED
package/src/main.js
CHANGED
|
@@ -141,6 +141,115 @@ function readLongBracket(input, index) {
|
|
|
141
141
|
};
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
function decodeStringToken(token) {
|
|
145
|
+
const quote = token[0];
|
|
146
|
+
if (quote !== '"' && quote !== "'" && quote !== "`") {
|
|
147
|
+
return token;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function keepEscape(code) {
|
|
151
|
+
// Only de-uglify printable ASCII; leave control bytes and chars that
|
|
152
|
+
// would need re-escaping (quote/backslash) as their original escapes.
|
|
153
|
+
if (code < 0x20 || code > 0x7e) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
const ch = String.fromCharCode(code);
|
|
157
|
+
return ch !== '"' && ch !== "'" && ch !== "`" && ch !== "\\";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Pass 1: split into units — final text, or a preserved decimal escape
|
|
161
|
+
// (kept as { dec } so padding can be decided once we know what follows).
|
|
162
|
+
const units = [];
|
|
163
|
+
function pushText(text) {
|
|
164
|
+
const last = units[units.length - 1];
|
|
165
|
+
if (last && last.text !== undefined) {
|
|
166
|
+
last.text += text;
|
|
167
|
+
} else {
|
|
168
|
+
units.push({ text });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let i = 0;
|
|
173
|
+
while (i < token.length) {
|
|
174
|
+
const ch = token[i];
|
|
175
|
+
if (ch !== "\\") {
|
|
176
|
+
pushText(ch);
|
|
177
|
+
i++;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const next = token[i + 1];
|
|
182
|
+
const hexMatch = next === "x" && /^[0-9A-Fa-f]{2}/.test(token.slice(i + 2, i + 4))
|
|
183
|
+
? token.slice(i + 2, i + 4)
|
|
184
|
+
: null;
|
|
185
|
+
if (hexMatch) {
|
|
186
|
+
const code = parseInt(hexMatch, 16);
|
|
187
|
+
if (keepEscape(code)) {
|
|
188
|
+
pushText(String.fromCharCode(code));
|
|
189
|
+
} else {
|
|
190
|
+
// Hex escapes are fixed-width (\xHH), so never ambiguous.
|
|
191
|
+
pushText("\\x" + hexMatch);
|
|
192
|
+
}
|
|
193
|
+
i += 4;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const decMatch = /^[0-9]{1,3}/.exec(token.slice(i + 1, i + 4));
|
|
198
|
+
if (decMatch) {
|
|
199
|
+
const digits = decMatch[0];
|
|
200
|
+
const code = parseInt(digits, 10);
|
|
201
|
+
i += 1 + digits.length;
|
|
202
|
+
if (code <= 0xff && keepEscape(code)) {
|
|
203
|
+
pushText(String.fromCharCode(code));
|
|
204
|
+
} else {
|
|
205
|
+
units.push({ dec: Math.min(code, 0xff) });
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Preserve any other escape sequence verbatim (\n, \t, \\, \" ...).
|
|
211
|
+
pushText(ch + (next === undefined ? "" : next));
|
|
212
|
+
i += next === undefined ? 1 : 2;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Pass 2: serialize. A preserved decimal escape only needs zero-padding to
|
|
216
|
+
// 3 digits when the next output char is a digit (which would otherwise
|
|
217
|
+
// merge into a single, possibly >255, escape).
|
|
218
|
+
let result = "";
|
|
219
|
+
for (let u = 0; u < units.length; u++) {
|
|
220
|
+
const unit = units[u];
|
|
221
|
+
if (unit.text !== undefined) {
|
|
222
|
+
result += unit.text;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const nextUnit = units[u + 1];
|
|
227
|
+
const nextChar = nextUnit && nextUnit.text !== undefined ? nextUnit.text[0] : "";
|
|
228
|
+
result += "\\" + (/[0-9]/.test(nextChar)
|
|
229
|
+
? String(unit.dec).padStart(3, "0")
|
|
230
|
+
: String(unit.dec));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function normalizeNumberToken(token) {
|
|
237
|
+
// Underscore digit separators are stripped (a no-op when none are present).
|
|
238
|
+
const stripped = token.replace(/_/g, "");
|
|
239
|
+
|
|
240
|
+
const hexMatch = /^0[xX]([0-9A-Fa-f]+)$/.exec(stripped);
|
|
241
|
+
if (hexMatch) {
|
|
242
|
+
return BigInt("0x" + hexMatch[1]).toString(10);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const binMatch = /^0[bB]([01]+)$/.exec(stripped);
|
|
246
|
+
if (binMatch) {
|
|
247
|
+
return BigInt("0b" + binMatch[1]).toString(10);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return stripped;
|
|
251
|
+
}
|
|
252
|
+
|
|
144
253
|
function tokenize(input) {
|
|
145
254
|
const tokens = [];
|
|
146
255
|
let i = 0;
|
|
@@ -174,17 +283,26 @@ function tokenize(input) {
|
|
|
174
283
|
if (ch === '"' || ch === "'" || ch === "`") {
|
|
175
284
|
let value = ch;
|
|
176
285
|
i++;
|
|
286
|
+
let escaped = false;
|
|
177
287
|
|
|
178
288
|
while (i < input.length) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
289
|
+
const c = input[i];
|
|
290
|
+
value += c;
|
|
291
|
+
i++;
|
|
292
|
+
if (escaped) {
|
|
293
|
+
escaped = false;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (c === "\\") {
|
|
297
|
+
escaped = true;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (c === ch) {
|
|
182
301
|
break;
|
|
183
302
|
}
|
|
184
|
-
i++;
|
|
185
303
|
}
|
|
186
304
|
|
|
187
|
-
tokens.push(value);
|
|
305
|
+
tokens.push(decodeStringToken(value));
|
|
188
306
|
continue;
|
|
189
307
|
}
|
|
190
308
|
|
|
@@ -217,7 +335,7 @@ function tokenize(input) {
|
|
|
217
335
|
while (end < input.length && /[A-Fa-f0-9_xX.]/.test(input[end])) {
|
|
218
336
|
end++;
|
|
219
337
|
}
|
|
220
|
-
tokens.push(input.slice(i, end));
|
|
338
|
+
tokens.push(normalizeNumberToken(input.slice(i, end)));
|
|
221
339
|
i = end;
|
|
222
340
|
continue;
|
|
223
341
|
}
|
|
@@ -241,6 +359,26 @@ function isLiteral(token) {
|
|
|
241
359
|
return /^["'`]/.test(token) || /^\d/.test(token) || token === "..." || token === "true" || token === "false" || token === "nil";
|
|
242
360
|
}
|
|
243
361
|
|
|
362
|
+
const keywordsSpacedBeforeParen = new Set([
|
|
363
|
+
"if",
|
|
364
|
+
"elseif",
|
|
365
|
+
"while",
|
|
366
|
+
"until",
|
|
367
|
+
"return",
|
|
368
|
+
"and",
|
|
369
|
+
"or",
|
|
370
|
+
"not",
|
|
371
|
+
"in"
|
|
372
|
+
]);
|
|
373
|
+
|
|
374
|
+
function isValueEnd(token) {
|
|
375
|
+
return (isIdentifier(token) && !reservedKeywords.has(token)) ||
|
|
376
|
+
isLiteral(token) ||
|
|
377
|
+
token === ")" ||
|
|
378
|
+
token === "]" ||
|
|
379
|
+
token === "}";
|
|
380
|
+
}
|
|
381
|
+
|
|
244
382
|
function canEndStatement(token) {
|
|
245
383
|
return (isIdentifier(token) && !reservedKeywords.has(token)) || isLiteral(token) || token === ")" || token === "]" || token === "}" || token === "end" || token === "break" || token === "continue";
|
|
246
384
|
}
|
|
@@ -271,6 +409,15 @@ function needsSpace(prev, current) {
|
|
|
271
409
|
}
|
|
272
410
|
|
|
273
411
|
if (current === "(") {
|
|
412
|
+
// Control-flow keywords read better with a space before the paren
|
|
413
|
+
// (`if (`, `while (`, `elseif (`), but calls like `function(` and
|
|
414
|
+
// `foo(` stay tight.
|
|
415
|
+
return keywordsSpacedBeforeParen.has(prev);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (current === "[" && isValueEnd(prev)) {
|
|
419
|
+
// Indexing: `A[x]`, `t.k[x]`, `f()[x]` — no space. Table-constructor
|
|
420
|
+
// keys (`{ [k] = v }`) keep prev as `{`/`,`, so they are unaffected.
|
|
274
421
|
return false;
|
|
275
422
|
}
|
|
276
423
|
|
|
@@ -293,22 +440,56 @@ function needsSpace(prev, current) {
|
|
|
293
440
|
return true;
|
|
294
441
|
}
|
|
295
442
|
|
|
296
|
-
function
|
|
443
|
+
function isGenericOpen(tokens, index) {
|
|
444
|
+
// tokens[index] is "<". Treat it as a generic-parameter list only in a
|
|
445
|
+
// declaration context: `function<T>`, `function foo<T>`, `type X<T>`.
|
|
446
|
+
const prev = tokens[index - 1];
|
|
447
|
+
if (prev === "function" || prev === "type") {
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const prev2 = tokens[index - 2];
|
|
452
|
+
return (prev2 === "function" || prev2 === "type") &&
|
|
453
|
+
isIdentifier(prev) &&
|
|
454
|
+
!reservedKeywords.has(prev);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function joinTokens(tokens) {
|
|
297
458
|
let text = "";
|
|
298
459
|
let prev = null;
|
|
460
|
+
let genericDepth = 0;
|
|
299
461
|
|
|
300
|
-
for (
|
|
301
|
-
|
|
462
|
+
for (let idx = 0; idx < tokens.length; idx++) {
|
|
463
|
+
const token = tokens[idx];
|
|
464
|
+
|
|
465
|
+
const inGeneric = genericDepth > 0;
|
|
466
|
+
const opensGeneric = !inGeneric && token === "<" && isGenericOpen(tokens, idx);
|
|
467
|
+
|
|
468
|
+
if (prev !== null && !inGeneric && !opensGeneric && needsSpace(prev, token)) {
|
|
302
469
|
text += " ";
|
|
303
470
|
}
|
|
304
471
|
text += token;
|
|
472
|
+
|
|
473
|
+
// Track generic-bracket nesting so the whole `<...>` stays tight.
|
|
474
|
+
if (opensGeneric || (inGeneric && token === "<")) {
|
|
475
|
+
genericDepth++;
|
|
476
|
+
} else if (inGeneric && token === ">") {
|
|
477
|
+
genericDepth--;
|
|
478
|
+
} else if (inGeneric && token === ">>") {
|
|
479
|
+
genericDepth = Math.max(0, genericDepth - 2);
|
|
480
|
+
}
|
|
481
|
+
|
|
305
482
|
prev = token;
|
|
306
483
|
}
|
|
307
484
|
|
|
308
|
-
return text
|
|
485
|
+
return text;
|
|
309
486
|
}
|
|
310
487
|
|
|
311
|
-
function
|
|
488
|
+
function renderLine(tokens) {
|
|
489
|
+
return joinTokens(tokens).trim();
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function layout(input) {
|
|
312
493
|
const tokens = tokenize(input);
|
|
313
494
|
const lines = [];
|
|
314
495
|
let current = [];
|
|
@@ -329,7 +510,7 @@ function beautify(input) {
|
|
|
329
510
|
|
|
330
511
|
lines.push({
|
|
331
512
|
depth: Math.max(0, depth),
|
|
332
|
-
|
|
513
|
+
tokens: current
|
|
333
514
|
});
|
|
334
515
|
current = [];
|
|
335
516
|
}
|
|
@@ -381,7 +562,7 @@ function beautify(input) {
|
|
|
381
562
|
flushCurrent();
|
|
382
563
|
lines.push({
|
|
383
564
|
depth: Math.max(0, depth),
|
|
384
|
-
|
|
565
|
+
tokens: [token]
|
|
385
566
|
});
|
|
386
567
|
continue;
|
|
387
568
|
}
|
|
@@ -494,6 +675,23 @@ function beautify(input) {
|
|
|
494
675
|
}
|
|
495
676
|
|
|
496
677
|
if (token === ";") {
|
|
678
|
+
const table = currentTable();
|
|
679
|
+
const inTable = table &&
|
|
680
|
+
braceDepth === table.brace &&
|
|
681
|
+
parenDepth === table.paren &&
|
|
682
|
+
bracketDepth === table.bracket &&
|
|
683
|
+
blockBases.length === table.blockDepth;
|
|
684
|
+
|
|
685
|
+
if (inTable) {
|
|
686
|
+
// Inside a table constructor a `;` is just a field separator;
|
|
687
|
+
// normalize it to `,`.
|
|
688
|
+
current[current.length - 1] = ",";
|
|
689
|
+
if (table.multiline) {
|
|
690
|
+
flushCurrent();
|
|
691
|
+
}
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
|
|
497
695
|
flushCurrent();
|
|
498
696
|
continue;
|
|
499
697
|
}
|
|
@@ -558,27 +756,78 @@ function beautify(input) {
|
|
|
558
756
|
|
|
559
757
|
flushCurrent();
|
|
560
758
|
|
|
561
|
-
return lines
|
|
562
|
-
|
|
759
|
+
return lines;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function beautify(input) {
|
|
763
|
+
return layout(input)
|
|
764
|
+
.map(line => `${indent.repeat(line.depth)}${renderLine(line.tokens)}`.trimEnd())
|
|
563
765
|
.join("\n");
|
|
564
766
|
}
|
|
565
767
|
|
|
768
|
+
function isIdentChar(ch) {
|
|
769
|
+
return /[A-Za-z0-9_]/.test(ch);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
function minifyNeedsSpace(prev, current) {
|
|
773
|
+
if (prev === null) {
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
const lastA = prev[prev.length - 1];
|
|
778
|
+
const firstB = current[0];
|
|
779
|
+
|
|
780
|
+
// Two word/number tokens would fuse into one (`local`+`x`, `1`+`e`).
|
|
781
|
+
if (isIdentChar(lastA) && isIdentChar(firstB)) {
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// A numeric literal followed by `.`/`..` is ambiguous (`1`+`..` -> `1..`).
|
|
786
|
+
if (/^[0-9]/.test(prev) && firstB === ".") {
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Avoid two operator chars fusing into a longer token (`-`+`-` -> `--`
|
|
791
|
+
// comment, `.`+`.` -> `..`, `<`+`=` -> `<=`, `:`+`:` -> `::`, ...).
|
|
792
|
+
const pair = lastA + firstB;
|
|
793
|
+
if (pair === "--" || multiCharTokens.some(token => token.startsWith(pair))) {
|
|
794
|
+
return true;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
|
|
566
800
|
function minify(input) {
|
|
567
|
-
const
|
|
568
|
-
|
|
801
|
+
const lines = layout(input).filter(
|
|
802
|
+
line => !(line.tokens.length === 1 && isComment(line.tokens[0]))
|
|
803
|
+
);
|
|
569
804
|
|
|
570
|
-
let
|
|
805
|
+
let result = "";
|
|
571
806
|
let prev = null;
|
|
572
807
|
|
|
573
|
-
for (const
|
|
574
|
-
|
|
575
|
-
|
|
808
|
+
for (const line of lines) {
|
|
809
|
+
const tokens = line.tokens;
|
|
810
|
+
for (let k = 0; k < tokens.length; k++) {
|
|
811
|
+
const token = tokens[k];
|
|
812
|
+
|
|
813
|
+
if (prev !== null) {
|
|
814
|
+
const callable = prev === ")" || prev === "]" ||
|
|
815
|
+
(isIdentifier(prev) && !reservedKeywords.has(prev));
|
|
816
|
+
if (k === 0 && token === "(" && callable) {
|
|
817
|
+
// A new statement starting with `(` after a prefix-expression
|
|
818
|
+
// would be parsed as a call continuation; separate with `;`.
|
|
819
|
+
result += ";";
|
|
820
|
+
} else if (minifyNeedsSpace(prev, token)) {
|
|
821
|
+
result += " ";
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
result += token;
|
|
826
|
+
prev = token;
|
|
576
827
|
}
|
|
577
|
-
text += token;
|
|
578
|
-
prev = token;
|
|
579
828
|
}
|
|
580
829
|
|
|
581
|
-
return
|
|
830
|
+
return result;
|
|
582
831
|
}
|
|
583
832
|
|
|
584
833
|
module.exports = { beautify, minify };
|