@tilua/compiler 4.0.0 → 5.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/README.md +98 -9
- package/dist/{chunk-VZ7XC4BG.js → chunk-3J2QMA4W.js} +601 -114
- package/dist/cli.cjs +667 -158
- package/dist/cli.js +32 -9
- package/dist/index.cjs +623 -135
- package/dist/index.d.cts +41 -6
- package/dist/index.d.ts +41 -6
- package/dist/index.js +6 -4
- package/package.json +2 -2
- package/runtime/array.luau +12 -4
- package/runtime/console.luau +227 -0
- package/runtime/result.luau +122 -0
package/dist/index.cjs
CHANGED
|
@@ -33,9 +33,9 @@ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${_
|
|
|
33
33
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
34
34
|
|
|
35
35
|
// src/bundle.ts
|
|
36
|
-
var
|
|
36
|
+
var import_node_fs3 = require("fs");
|
|
37
37
|
var import_node_path2 = require("path");
|
|
38
|
-
var
|
|
38
|
+
var import_parser7 = require("@tilua/parser");
|
|
39
39
|
var import_luau_parser2 = require("luau-parser");
|
|
40
40
|
|
|
41
41
|
// src/config.ts
|
|
@@ -50,7 +50,7 @@ function resolveConfig(input, entry, cwd = process.cwd()) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// src/lower.ts
|
|
53
|
-
var
|
|
53
|
+
var import_parser5 = require("@tilua/parser");
|
|
54
54
|
var import_luau_parser = require("luau-parser");
|
|
55
55
|
|
|
56
56
|
// src/luau.ts
|
|
@@ -212,6 +212,77 @@ var plugin = {
|
|
|
212
212
|
};
|
|
213
213
|
var languageLowering = { plugin, from: "tilua" };
|
|
214
214
|
|
|
215
|
+
// src/standard.ts
|
|
216
|
+
var import_node_fs2 = require("fs");
|
|
217
|
+
var import_parser4 = require("@tilua/parser");
|
|
218
|
+
var read2 = (name) => (0, import_node_fs2.readFileSync)(new URL(`../runtime/${name}`, importMetaUrl), "utf8");
|
|
219
|
+
var console = read2("console.luau");
|
|
220
|
+
var result = read2("result.luau");
|
|
221
|
+
var place = (at) => (0, import_parser4.luauString)(at.file === void 0 ? `line ${at.line}` : `${at.file}:${at.line}`);
|
|
222
|
+
var tag = (at) => (0, import_parser4.luauString)(`[${at.file === void 0 ? `line ${at.line}` : `${at.file.replace(/\.tilua$/, "")}:${at.line}`}]:`);
|
|
223
|
+
function consoleMeta(args) {
|
|
224
|
+
const fields = [];
|
|
225
|
+
args.forEach((argument, index2) => {
|
|
226
|
+
if (argument.spread || argument.typeText === void 0 || !(0, import_parser4.isFunctionType)(argument.type)) return;
|
|
227
|
+
const written = argument.declaration ?? argument.source;
|
|
228
|
+
fields.push(`[${index2 + 1}] = { ${(0, import_parser4.luauString)(argument.typeText)}, ${(0, import_parser4.luauString)(written)} }`);
|
|
229
|
+
});
|
|
230
|
+
return fields.length ? `{ ${fields.join(", ")} }` : "nil";
|
|
231
|
+
}
|
|
232
|
+
var CONSOLE_METHODS = /* @__PURE__ */ new Set(["log", "warn", "error"]);
|
|
233
|
+
var REPLACED = /* @__PURE__ */ new Map([
|
|
234
|
+
["pcall", "pcall"],
|
|
235
|
+
["xpcall", "xpcall"],
|
|
236
|
+
["ypcall", "pcall"],
|
|
237
|
+
["coroutine.resume", "resume"],
|
|
238
|
+
["string.find", "find"],
|
|
239
|
+
["string.match", "match"],
|
|
240
|
+
["string.gsub", "gsub"],
|
|
241
|
+
["string.byte", "byte"],
|
|
242
|
+
["math.modf", "modf"],
|
|
243
|
+
["math.frexp", "frexp"],
|
|
244
|
+
["next", "next"],
|
|
245
|
+
["select", "select"],
|
|
246
|
+
["unpack", "unpack"],
|
|
247
|
+
["table.unpack", "unpack"],
|
|
248
|
+
["loadstring", "loadstring"],
|
|
249
|
+
["pairs", "pairs"],
|
|
250
|
+
["ipairs", "ipairs"],
|
|
251
|
+
["string.gmatch", "gmatch"]
|
|
252
|
+
]);
|
|
253
|
+
var plugin2 = {
|
|
254
|
+
runtime: { console, result },
|
|
255
|
+
methodCall({ method, receiverGlobal, at, arguments: args, use }) {
|
|
256
|
+
if (receiverGlobal === "console" && CONSOLE_METHODS.has(method)) {
|
|
257
|
+
return {
|
|
258
|
+
callee: `${use("console")}.${method}`,
|
|
259
|
+
prepend: [tag(at), consoleMeta(args)],
|
|
260
|
+
passReceiver: false
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return void 0;
|
|
264
|
+
},
|
|
265
|
+
// `print(x)` and `warn(x)` are left exactly as written: the place a line
|
|
266
|
+
// came from is what `console:log` / `console:warn` are for. `error` has to
|
|
267
|
+
// put the place inside the message it raises, so it goes through the
|
|
268
|
+
// runtime.
|
|
269
|
+
globalCall({ name, at, use }) {
|
|
270
|
+
if (name === "error") return { callee: `${use("console")}.raise`, prepend: [place(at)] };
|
|
271
|
+
const replaced = REPLACED.get(name);
|
|
272
|
+
if (replaced) return { callee: `${use("result")}.${replaced}` };
|
|
273
|
+
return void 0;
|
|
274
|
+
},
|
|
275
|
+
// Read as a value there is no call site to write down; the runtime finds
|
|
276
|
+
// one on the stack.
|
|
277
|
+
globalValue({ name, use }) {
|
|
278
|
+
if (name === "error") return `${use("console")}.raiseValue`;
|
|
279
|
+
const replaced = REPLACED.get(name);
|
|
280
|
+
if (replaced) return `${use("result")}.${replaced}`;
|
|
281
|
+
return void 0;
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
var standardLowering = { plugin: plugin2, from: "tilua (standard library)" };
|
|
285
|
+
|
|
215
286
|
// src/lower.ts
|
|
216
287
|
function lower(program2, scopes, options = {}) {
|
|
217
288
|
return new Lowerer(program2, scopes, options).run();
|
|
@@ -301,6 +372,10 @@ var Lowerer = class {
|
|
|
301
372
|
}
|
|
302
373
|
return identifier(local2);
|
|
303
374
|
}
|
|
375
|
+
/** `table.move`. Lua 5.1 has none, and gets a function that does the same. */
|
|
376
|
+
move() {
|
|
377
|
+
return this.lua51 ? this.helper("move") : member(this.builtin("table"), "move");
|
|
378
|
+
}
|
|
304
379
|
helper(kind) {
|
|
305
380
|
let name = this.helpers.get(kind);
|
|
306
381
|
if (!name) {
|
|
@@ -401,7 +476,7 @@ var Lowerer = class {
|
|
|
401
476
|
const type = this.options.types?.typeOf.get(argument);
|
|
402
477
|
return {
|
|
403
478
|
type,
|
|
404
|
-
typeText: type ? (0,
|
|
479
|
+
typeText: type ? (0, import_parser5.formatType)(type) : void 0,
|
|
405
480
|
source: this.writtenSource(argument),
|
|
406
481
|
declaration: declaration ? this.writtenSource(declaration) : void 0,
|
|
407
482
|
spread: argument.type === "SpreadElement"
|
|
@@ -423,7 +498,11 @@ var Lowerer = class {
|
|
|
423
498
|
/** The language's own lowering, then each library's, in load order. A
|
|
424
499
|
* runtime is named by its lowering's place here (`loweringRuntime`). */
|
|
425
500
|
get lowerings() {
|
|
426
|
-
return this.allLowerings ??= [
|
|
501
|
+
return this.allLowerings ??= [
|
|
502
|
+
languageLowering,
|
|
503
|
+
...this.options.noLib ? [] : [standardLowering],
|
|
504
|
+
...this.options.lowerings ?? []
|
|
505
|
+
];
|
|
427
506
|
}
|
|
428
507
|
allLowerings;
|
|
429
508
|
/** What `receiver:method(...)` becomes, when Luau alone would not run it.
|
|
@@ -437,22 +516,28 @@ var Lowerer = class {
|
|
|
437
516
|
* (`console:log`), the last library loaded first. No answer leaves an
|
|
438
517
|
* ordinary Luau method call: `text:upper()` reaches Lua's own. */
|
|
439
518
|
loweredMethodCall(node) {
|
|
519
|
+
return this.loweredMethod(node.method.name, this.options.types?.methodSources.get(node), node.object, node.arguments, node.method, node);
|
|
520
|
+
}
|
|
521
|
+
/** `loweredMethodCall`, for a method named `method` of what `object`
|
|
522
|
+
* holds — called with `:` on it, or read off it with `.` and handed its
|
|
523
|
+
* `self` like any other argument. `args` are the arguments after the
|
|
524
|
+
* receiver; a read that is not called has none. */
|
|
525
|
+
loweredMethod(method, source, object2, args, name, node) {
|
|
440
526
|
const lowerings = this.lowerings;
|
|
441
|
-
const source = this.options.types?.methodSources.get(node);
|
|
442
527
|
const asked = source?.origin === "language" ? [0] : source?.origin === "library" ? lowerings.flatMap((l, i) => l.library === source.library ? [i] : []) : source?.origin === "program" ? [] : lowerings.map((_, i) => i).slice(1).reverse();
|
|
443
528
|
if (!asked.some((i) => lowerings[i].plugin.methodCall)) return void 0;
|
|
444
|
-
const receiver = this.options.types?.typeOf.get(
|
|
445
|
-
const receiverGlobal = this.globalName(
|
|
446
|
-
const info = this.lazyArguments(
|
|
529
|
+
const receiver = this.options.types?.typeOf.get(object2);
|
|
530
|
+
const receiverGlobal = this.globalName(object2);
|
|
531
|
+
const info = this.lazyArguments(args);
|
|
447
532
|
for (const i of asked) {
|
|
448
|
-
const
|
|
449
|
-
if (!
|
|
450
|
-
const answer =
|
|
451
|
-
method
|
|
533
|
+
const plugin3 = lowerings[i].plugin;
|
|
534
|
+
if (!plugin3.methodCall) continue;
|
|
535
|
+
const answer = plugin3.methodCall({
|
|
536
|
+
method,
|
|
452
537
|
receiver,
|
|
453
538
|
receiverGlobal,
|
|
454
|
-
argumentCount:
|
|
455
|
-
at: this.callSite(
|
|
539
|
+
argumentCount: args.length,
|
|
540
|
+
at: this.callSite(name),
|
|
456
541
|
get arguments() {
|
|
457
542
|
return info();
|
|
458
543
|
},
|
|
@@ -476,9 +561,9 @@ var Lowerer = class {
|
|
|
476
561
|
if (name === void 0) return void 0;
|
|
477
562
|
const info = this.lazyArguments(node.arguments);
|
|
478
563
|
for (let i = lowerings.length - 1; i >= 0; i--) {
|
|
479
|
-
const
|
|
480
|
-
if (!
|
|
481
|
-
const answer =
|
|
564
|
+
const plugin3 = lowerings[i].plugin;
|
|
565
|
+
if (!plugin3.globalCall) continue;
|
|
566
|
+
const answer = plugin3.globalCall({
|
|
482
567
|
name,
|
|
483
568
|
at: this.callSite(node.callee),
|
|
484
569
|
get arguments() {
|
|
@@ -530,6 +615,31 @@ var Lowerer = class {
|
|
|
530
615
|
const out = [];
|
|
531
616
|
const runtimes = this.runtimeDefinitions();
|
|
532
617
|
if (this.helpers.has("concat")) this.helper("array");
|
|
618
|
+
if (this.helpers.has("concat")) this.move();
|
|
619
|
+
const move = this.helpers.get("move");
|
|
620
|
+
if (move) {
|
|
621
|
+
out.push(...(0, import_luau_parser.parse)(`
|
|
622
|
+
local function ${move}(from, first, last, at, to)
|
|
623
|
+
for i = first, last do
|
|
624
|
+
to[at + i - first] = from[i]
|
|
625
|
+
end
|
|
626
|
+
return to
|
|
627
|
+
end`).body.statements);
|
|
628
|
+
}
|
|
629
|
+
const each = this.helpers.get("each");
|
|
630
|
+
if (each) {
|
|
631
|
+
out.push(...(0, import_luau_parser.parse)(`
|
|
632
|
+
local function ${each}(source, ...)
|
|
633
|
+
if ${this.builtin("type").name}(source) == "function" then
|
|
634
|
+
return source, ...
|
|
635
|
+
end
|
|
636
|
+
local meta = ${this.builtin("getmetatable").name}(source)
|
|
637
|
+
if meta ~= nil and meta.__iter ~= nil then
|
|
638
|
+
return meta.__iter(source)
|
|
639
|
+
end
|
|
640
|
+
return ${this.builtin("next").name}, source
|
|
641
|
+
end`).body.statements);
|
|
642
|
+
}
|
|
533
643
|
const marker = this.helpers.get("array");
|
|
534
644
|
const typeOf = this.helpers.get("typeof");
|
|
535
645
|
if (marker || typeOf) {
|
|
@@ -568,6 +678,49 @@ local function ${typeOf}(value, known)
|
|
|
568
678
|
end`).body.statements);
|
|
569
679
|
}
|
|
570
680
|
}
|
|
681
|
+
const instanceOf = this.helpers.get("instanceof");
|
|
682
|
+
if (instanceOf) {
|
|
683
|
+
out.push(...(0, import_luau_parser.parse)(`
|
|
684
|
+
local function ${instanceOf}(value, class)
|
|
685
|
+
if ${this.builtin("type").name}(value) ~= "table" then
|
|
686
|
+
return false
|
|
687
|
+
end
|
|
688
|
+
local current = value.ClassObject
|
|
689
|
+
while current ~= nil do
|
|
690
|
+
if current == class then
|
|
691
|
+
return true
|
|
692
|
+
end
|
|
693
|
+
current = current.ParentClass
|
|
694
|
+
end
|
|
695
|
+
return false
|
|
696
|
+
end`).body.statements);
|
|
697
|
+
}
|
|
698
|
+
const accessors = this.helpers.get("accessors");
|
|
699
|
+
if (accessors) {
|
|
700
|
+
out.push(...(0, import_luau_parser.parse)(`
|
|
701
|
+
local function ${accessors}(target, getters, setters)
|
|
702
|
+
return ${this.builtin("setmetatable").name}(target, {
|
|
703
|
+
__index = function(self, key)
|
|
704
|
+
local get = getters[key]
|
|
705
|
+
if get ~= nil then
|
|
706
|
+
return get(self)
|
|
707
|
+
end
|
|
708
|
+
return nil
|
|
709
|
+
end,
|
|
710
|
+
__newindex = function(self, key, value)
|
|
711
|
+
local set = setters[key]
|
|
712
|
+
if set ~= nil then
|
|
713
|
+
set(self, value)
|
|
714
|
+
return
|
|
715
|
+
end
|
|
716
|
+
if getters[key] ~= nil then
|
|
717
|
+
${this.builtin("error").name}("Cannot assign to '" .. ${this.builtin("tostring").name}(key) .. "': it has a getter and no setter", 2)
|
|
718
|
+
end
|
|
719
|
+
${this.builtin("rawset").name}(self, key, value)
|
|
720
|
+
end,
|
|
721
|
+
})
|
|
722
|
+
end`).body.statements);
|
|
723
|
+
}
|
|
571
724
|
const assign2 = this.helpers.get("assign");
|
|
572
725
|
if (assign2) {
|
|
573
726
|
out.push(localFunction(assign2, functionBody(["target"], [
|
|
@@ -589,7 +742,7 @@ end`).body.statements);
|
|
|
589
742
|
local(["result"], [this.array(table([]))]),
|
|
590
743
|
numericFor("i", number(1), selectCount(this.builtin("select")), [
|
|
591
744
|
local(["part"], [call(this.builtin("select"), [identifier("i"), vararg()])]),
|
|
592
|
-
callStatement(call(
|
|
745
|
+
callStatement(call(this.move(), [
|
|
593
746
|
identifier("part"),
|
|
594
747
|
number(1),
|
|
595
748
|
unary("#", identifier("part")),
|
|
@@ -606,8 +759,8 @@ end`).body.statements);
|
|
|
606
759
|
runtimeDefinitions() {
|
|
607
760
|
const out = [];
|
|
608
761
|
for (const { index: index2, runtime: runtime2, name } of this.loweringUsed) {
|
|
609
|
-
const { plugin:
|
|
610
|
-
const source =
|
|
762
|
+
const { plugin: plugin3, from } = this.lowerings[index2];
|
|
763
|
+
const source = plugin3.runtime?.[runtime2];
|
|
611
764
|
if (source === void 0) {
|
|
612
765
|
this.diagnostics.push({
|
|
613
766
|
message: `'${from}' asked for a runtime it does not have: '${runtime2}'`,
|
|
@@ -702,6 +855,7 @@ end`).body.statements);
|
|
|
702
855
|
names.push("default");
|
|
703
856
|
break;
|
|
704
857
|
case "ExportNamedStatement": {
|
|
858
|
+
if (statement.typeOnly) break;
|
|
705
859
|
if (statement.source) {
|
|
706
860
|
const key = resolve4(statement.source);
|
|
707
861
|
if (key === void 0) break;
|
|
@@ -785,6 +939,26 @@ end`).body.statements);
|
|
|
785
939
|
for (let i = 0; i < locals.length; i += 100) declarations.push(local(locals.slice(i, i + 100), []));
|
|
786
940
|
return [...declarations, ...hoisted, ...requires, ...body];
|
|
787
941
|
}
|
|
942
|
+
/** `import("./x")`: the bundle's `import`, called with the module's name.
|
|
943
|
+
* It answers the module's exports table — what `import * as` names. */
|
|
944
|
+
importExpression(node) {
|
|
945
|
+
const context = this.options.module;
|
|
946
|
+
if (!context) {
|
|
947
|
+
this.report(node, "import() needs a bundle: build the project with @tilua/compiler");
|
|
948
|
+
return nil();
|
|
949
|
+
}
|
|
950
|
+
const source = node.source;
|
|
951
|
+
if (source.type !== "StringLiteral") {
|
|
952
|
+
this.report(source, "import() takes a string literal: which module it loads is decided when the project is built");
|
|
953
|
+
return nil();
|
|
954
|
+
}
|
|
955
|
+
const key = context.resolve(source.value, true);
|
|
956
|
+
if (key === void 0) {
|
|
957
|
+
this.report(source, `Cannot find module '${source.value}'`);
|
|
958
|
+
return nil();
|
|
959
|
+
}
|
|
960
|
+
return call(context.import ?? context.require, [string(key)]);
|
|
961
|
+
}
|
|
788
962
|
isRewritten(node) {
|
|
789
963
|
const binding = this.bindingByDeclaration.get(node);
|
|
790
964
|
return binding !== void 0 && this.rewrites.has(binding.id);
|
|
@@ -931,7 +1105,7 @@ end`).body.statements);
|
|
|
931
1105
|
case "VariableDeclaration":
|
|
932
1106
|
return this.variableDeclaration(node, "declare");
|
|
933
1107
|
case "FunctionDeclaration":
|
|
934
|
-
return [{ ...localFunction(this.name(node.name.name), this.functionBody(node.func)), attributes: node.attributes }];
|
|
1108
|
+
return [{ ...localFunction(this.name(node.name.name), this.functionBody(node.func)), attributes: this.lua51 ? void 0 : node.attributes }];
|
|
935
1109
|
case "FunctionDeclarationStatement":
|
|
936
1110
|
return [this.functionDeclarationStatement(node)];
|
|
937
1111
|
case "ClassDeclaration":
|
|
@@ -962,8 +1136,13 @@ end`).body.statements);
|
|
|
962
1136
|
// kept — Lua has no expression statement — and it cannot run
|
|
963
1137
|
// anything, since a call is a `CallStatement` instead, so dropping
|
|
964
1138
|
// it changes nothing but the noise.
|
|
965
|
-
case "ExpressionStatement":
|
|
966
|
-
|
|
1139
|
+
case "ExpressionStatement": {
|
|
1140
|
+
let expression = node.expression;
|
|
1141
|
+
while (expression.type === "ParenthesizedExpression") expression = expression.expression;
|
|
1142
|
+
if (expression.type !== "ImportExpression") return [];
|
|
1143
|
+
const call2 = this.importExpression(expression);
|
|
1144
|
+
return call2.type === "CallExpression" ? [this.from(callStatement(call2), node)] : [];
|
|
1145
|
+
}
|
|
967
1146
|
case "DoStatement":
|
|
968
1147
|
return [{ type: "DoStatement", body: this.block(node.body), ...spanOf(node) }];
|
|
969
1148
|
case "WhileStatement":
|
|
@@ -1015,7 +1194,11 @@ end`).body.statements);
|
|
|
1015
1194
|
let source = this.expression(node.iterator);
|
|
1016
1195
|
if (form.viaIter) source = methodCall(source, "__iter", []);
|
|
1017
1196
|
const variables = form.walks === "values" ? [this.names.fresh("_"), item] : [item];
|
|
1018
|
-
|
|
1197
|
+
let iterators = form.walks === "iteration" ? [call(this.builtin("unpack"), [source, number(1), number(3)])] : [source];
|
|
1198
|
+
if (this.lua51 && form.walks === "values") {
|
|
1199
|
+
const kind = form.viaIter ? void 0 : this.options.types?.typeOf.get(node.iterator)?.kind;
|
|
1200
|
+
iterators = [call(kind === "array" || kind === "tuple" ? this.builtin("ipairs") : this.helper("each"), [source])];
|
|
1201
|
+
}
|
|
1019
1202
|
return [genericFor(
|
|
1020
1203
|
variables,
|
|
1021
1204
|
iterators,
|
|
@@ -1268,7 +1451,8 @@ end
|
|
|
1268
1451
|
},
|
|
1269
1452
|
isMethod,
|
|
1270
1453
|
func,
|
|
1271
|
-
|
|
1454
|
+
// `@native` and the like are Luau's.
|
|
1455
|
+
attributes: this.lua51 ? void 0 : node.attributes,
|
|
1272
1456
|
...spanOf(node)
|
|
1273
1457
|
};
|
|
1274
1458
|
}
|
|
@@ -1369,7 +1553,7 @@ end
|
|
|
1369
1553
|
if (element) bind(element.value, index(source, number(i + 1)), element.default);
|
|
1370
1554
|
});
|
|
1371
1555
|
if (pattern.rest) {
|
|
1372
|
-
const elements = call(
|
|
1556
|
+
const elements = call(this.move(), [
|
|
1373
1557
|
source,
|
|
1374
1558
|
number(pattern.elements.length + 1),
|
|
1375
1559
|
unary("#", source),
|
|
@@ -1480,8 +1664,15 @@ end
|
|
|
1480
1664
|
case "ArrayExpression":
|
|
1481
1665
|
return this.arrayExpression(node);
|
|
1482
1666
|
case "BinaryExpression": {
|
|
1667
|
+
if (node.operator === "??") return this.nullish(node);
|
|
1483
1668
|
const left = this.expression(node.left);
|
|
1484
1669
|
const right = this.expression(node.right);
|
|
1670
|
+
if (node.operator === "in") {
|
|
1671
|
+
return parenthesized(binary("~=", index(this.keptToOne(right), this.keptToOne(left)), nil()));
|
|
1672
|
+
}
|
|
1673
|
+
if (node.operator === "instanceof") {
|
|
1674
|
+
return call(this.helper("instanceof"), [this.keptToOne(left), this.keptToOne(right)]);
|
|
1675
|
+
}
|
|
1485
1676
|
if (this.lua51 && node.operator === "//") return this.floorDivide(left, right);
|
|
1486
1677
|
return binary(node.operator, left, right);
|
|
1487
1678
|
}
|
|
@@ -1508,6 +1699,8 @@ end
|
|
|
1508
1699
|
}
|
|
1509
1700
|
case "ParenthesizedExpression":
|
|
1510
1701
|
return parenthesized(this.expression(node.expression));
|
|
1702
|
+
case "ImportExpression":
|
|
1703
|
+
return this.importExpression(node);
|
|
1511
1704
|
// Types only: the value is the expression itself.
|
|
1512
1705
|
case "TypeAssertionExpression":
|
|
1513
1706
|
case "SatisfiesExpression":
|
|
@@ -1561,7 +1754,7 @@ end
|
|
|
1561
1754
|
const last2 = list.length - 1;
|
|
1562
1755
|
return lowered.length && !many.has(list[last2]) ? [...lowered.slice(0, -1), this.keptToOne(lowered[last2])] : lowered;
|
|
1563
1756
|
}
|
|
1564
|
-
const unpack = (value) => call(member(this.builtin("table"), "unpack"), [value]);
|
|
1757
|
+
const unpack = (value) => call(this.lua51 ? this.builtin("unpack") : member(this.builtin("table"), "unpack"), [value]);
|
|
1565
1758
|
const last = list[list.length - 1];
|
|
1566
1759
|
if (spreads.length === 1 && last.type === "SpreadElement") {
|
|
1567
1760
|
return [...list.slice(0, -1).map((a) => this.expression(a)), unpack(this.expression(last.argument))];
|
|
@@ -1575,11 +1768,21 @@ end
|
|
|
1575
1768
|
/** One link of an access chain, read from `object`. */
|
|
1576
1769
|
link(node, object2) {
|
|
1577
1770
|
switch (node.type) {
|
|
1578
|
-
case "MemberExpression":
|
|
1771
|
+
case "MemberExpression": {
|
|
1772
|
+
if (this.options.types?.lengths.has(node)) return unary("#", object2);
|
|
1773
|
+
const source = this.options.types?.methodReads.get(node);
|
|
1774
|
+
const lowered = source && this.loweredMethod(node.property.name, source, node.object, [], node.property, node);
|
|
1775
|
+
if (lowered) {
|
|
1776
|
+
const forwarded = call(calleePath(lowered.callee), [...lowered.prepend, vararg()]);
|
|
1777
|
+
return functionExpression(functionBody(lowered.passReceiver ? [] : ["_"], [returns([forwarded])], true));
|
|
1778
|
+
}
|
|
1579
1779
|
return member(object2, node.property.name);
|
|
1780
|
+
}
|
|
1580
1781
|
case "IndexExpression":
|
|
1581
1782
|
return index(object2, this.expression(node.index));
|
|
1582
1783
|
case "CallExpression": {
|
|
1784
|
+
const method = this.methodCalled(node);
|
|
1785
|
+
if (method) return method;
|
|
1583
1786
|
const lowered = this.loweredGlobalCall(node);
|
|
1584
1787
|
const args = this.arguments(node.arguments);
|
|
1585
1788
|
if (!lowered) return call(object2, args);
|
|
@@ -1602,6 +1805,24 @@ end
|
|
|
1602
1805
|
}
|
|
1603
1806
|
}
|
|
1604
1807
|
}
|
|
1808
|
+
/** `names.filter(names, f)`: a method of the value's metatable, read with
|
|
1809
|
+
* `.` and handed its `self` as the first argument. Written as a call of
|
|
1810
|
+
* the function the method really is — `array.filter(names, f)` — where
|
|
1811
|
+
* whoever declared the metatable says so; `undefined` leaves an ordinary
|
|
1812
|
+
* call, which is right for a string's Lua methods (`text.upper(text)`
|
|
1813
|
+
* reaches Lua's own `string.upper`). */
|
|
1814
|
+
methodCalled(node) {
|
|
1815
|
+
const callee = node.callee;
|
|
1816
|
+
if (callee.type !== "MemberExpression") return void 0;
|
|
1817
|
+
const source = this.options.types?.methodReads.get(callee);
|
|
1818
|
+
if (!source) return void 0;
|
|
1819
|
+
const spreads = node.arguments.map((a, i) => a.type === "SpreadElement" ? i : -1).filter((i) => i >= 0);
|
|
1820
|
+
if (!node.arguments.length || spreads.includes(0) || spreads.some((i) => i !== node.arguments.length - 1)) return void 0;
|
|
1821
|
+
const lowered = this.loweredMethod(callee.property.name, source, callee.object, node.arguments.slice(1), callee.property, node);
|
|
1822
|
+
if (!lowered) return void 0;
|
|
1823
|
+
const [self, ...rest] = this.arguments(node.arguments);
|
|
1824
|
+
return call(calleePath(lowered.callee), [...lowered.prepend, ...lowered.passReceiver ? [self] : [], ...rest]);
|
|
1825
|
+
}
|
|
1605
1826
|
// --------------------------------------------------------
|
|
1606
1827
|
// Optional chains
|
|
1607
1828
|
// --------------------------------------------------------
|
|
@@ -1647,7 +1868,7 @@ end
|
|
|
1647
1868
|
const segments = this.segments(chain);
|
|
1648
1869
|
const base = this.expression(chain.base);
|
|
1649
1870
|
const last = chain.links[chain.links.length - 1];
|
|
1650
|
-
if (base.type === "Identifier" && segments.length === 1 && last.type !== "CallExpression" && last.type !== "MethodCallExpression") {
|
|
1871
|
+
if (!this.lua51 && base.type === "Identifier" && segments.length === 1 && last.type !== "CallExpression" && last.type !== "MethodCallExpression") {
|
|
1651
1872
|
return {
|
|
1652
1873
|
type: "IfElseExpression",
|
|
1653
1874
|
clauses: [{ condition: binary("==", base, nil()), body: nil() }],
|
|
@@ -1668,6 +1889,32 @@ end
|
|
|
1668
1889
|
const func = functionExpression(functionBody([], statements, passesVarargs));
|
|
1669
1890
|
return call(parenthesized(func), passesVarargs ? [vararg()] : []);
|
|
1670
1891
|
}
|
|
1892
|
+
/** `a ?? b`: `a`, or `b` where `a` is nil — read only then. A `false`
|
|
1893
|
+
* stays, which is what sets it apart from `a or b`. A name is tested
|
|
1894
|
+
* in place; anything else is read once, into a local. */
|
|
1895
|
+
nullish(node) {
|
|
1896
|
+
const left = this.keptToOne(this.expression(node.left));
|
|
1897
|
+
const right = this.keptToOne(this.expression(node.right));
|
|
1898
|
+
if (!this.lua51 && left.type === "Identifier") {
|
|
1899
|
+
return {
|
|
1900
|
+
type: "IfElseExpression",
|
|
1901
|
+
clauses: [{ condition: binary("~=", left, nil()), body: left }],
|
|
1902
|
+
alternate: right,
|
|
1903
|
+
line: left.line,
|
|
1904
|
+
column: left.column
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
const ref = this.names.fresh("value");
|
|
1908
|
+
const at = identifier(ref);
|
|
1909
|
+
const statements = [
|
|
1910
|
+
local([ref], [left]),
|
|
1911
|
+
ifThen(binary("~=", at, nil()), [returns([at])]),
|
|
1912
|
+
returns([right])
|
|
1913
|
+
];
|
|
1914
|
+
const passesVarargs = statements.some(usesVararg);
|
|
1915
|
+
const func = functionExpression(functionBody([], statements, passesVarargs));
|
|
1916
|
+
return call(parenthesized(func), passesVarargs ? [vararg()] : []);
|
|
1917
|
+
}
|
|
1671
1918
|
/** `` `${a} any` `` -> `("%s any"):format(tostring(a))` */
|
|
1672
1919
|
interpolatedString(node) {
|
|
1673
1920
|
let format = "";
|
|
@@ -1749,12 +1996,12 @@ end
|
|
|
1749
1996
|
}));
|
|
1750
1997
|
const alternate = this.expression(node.alternate);
|
|
1751
1998
|
if (clauses.every((c) => alwaysTruthy(c.body))) {
|
|
1752
|
-
let
|
|
1999
|
+
let result2 = alternate;
|
|
1753
2000
|
for (let i = clauses.length - 1; i >= 0; i--) {
|
|
1754
2001
|
const { condition, body } = clauses[i];
|
|
1755
|
-
|
|
2002
|
+
result2 = binary("or", binary("and", condition, body), result2);
|
|
1756
2003
|
}
|
|
1757
|
-
return parenthesized(
|
|
2004
|
+
return parenthesized(result2);
|
|
1758
2005
|
}
|
|
1759
2006
|
const statement = {
|
|
1760
2007
|
type: "IfStatement",
|
|
@@ -1811,11 +2058,20 @@ end
|
|
|
1811
2058
|
current = [];
|
|
1812
2059
|
parts.push(this.expression(f.argument));
|
|
1813
2060
|
break;
|
|
2061
|
+
case "TableFieldAccessor":
|
|
2062
|
+
break;
|
|
1814
2063
|
}
|
|
1815
2064
|
}
|
|
1816
|
-
|
|
1817
|
-
if (
|
|
1818
|
-
|
|
2065
|
+
let made;
|
|
2066
|
+
if (parts.length === 0) made = table(current);
|
|
2067
|
+
else {
|
|
2068
|
+
if (current.length) parts.push(table(current));
|
|
2069
|
+
made = call(this.helper("assign"), [table([]), ...parts]);
|
|
2070
|
+
}
|
|
2071
|
+
const accessors = node.fields.filter((f) => f.type === "TableFieldAccessor");
|
|
2072
|
+
if (!accessors.length) return made;
|
|
2073
|
+
const table2 = (kind) => table(accessors.filter((f) => f.kind === kind).map((f) => field(f.key.type === "Identifier" ? f.key.name : f.key.value, functionExpression(this.classFunctionBody(f.func)))));
|
|
2074
|
+
return call(this.helper("accessors"), [made, table2("get"), table2("set")]);
|
|
1819
2075
|
}
|
|
1820
2076
|
/** `[1, ...xs, 2]`. Without a spread it is a Luau sequence; with one, the
|
|
1821
2077
|
* runs of plain elements and the spread arrays are joined by `concat`. */
|
|
@@ -2072,7 +2328,7 @@ var CLASS_METAMETHODS = [
|
|
|
2072
2328
|
];
|
|
2073
2329
|
|
|
2074
2330
|
// src/linemap.ts
|
|
2075
|
-
var
|
|
2331
|
+
var import_parser6 = require("@tilua/parser");
|
|
2076
2332
|
function statementsOf(node) {
|
|
2077
2333
|
const out = [];
|
|
2078
2334
|
const visit = (value) => {
|
|
@@ -2105,7 +2361,7 @@ function buildLineMap(emitted, printed, origins) {
|
|
|
2105
2361
|
}
|
|
2106
2362
|
function lineMapSource(map) {
|
|
2107
2363
|
const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
|
|
2108
|
-
const quoted = (s) => (0,
|
|
2364
|
+
const quoted = (s) => (0, import_parser6.luauString)(s.replace(/\\/g, "/"));
|
|
2109
2365
|
return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
|
|
2110
2366
|
}
|
|
2111
2367
|
|
|
@@ -2117,15 +2373,15 @@ async function loadLowerings(modules) {
|
|
|
2117
2373
|
for (const module2 of modules) {
|
|
2118
2374
|
try {
|
|
2119
2375
|
const loaded = await import((0, import_node_url.pathToFileURL)(module2.file).href);
|
|
2120
|
-
const
|
|
2121
|
-
if (!
|
|
2376
|
+
const plugin3 = loaded.default;
|
|
2377
|
+
if (!plugin3 || typeof plugin3 !== "object") {
|
|
2122
2378
|
problems.push({
|
|
2123
2379
|
file: module2.file,
|
|
2124
2380
|
message: `'${module2.from}' lowering module has no default export`
|
|
2125
2381
|
});
|
|
2126
2382
|
continue;
|
|
2127
2383
|
}
|
|
2128
|
-
lowerings.push({ plugin:
|
|
2384
|
+
lowerings.push({ plugin: plugin3, from: module2.from, library: module2.library });
|
|
2129
2385
|
} catch (error) {
|
|
2130
2386
|
problems.push({
|
|
2131
2387
|
file: module2.file,
|
|
@@ -2152,16 +2408,17 @@ async function bundle(options) {
|
|
|
2152
2408
|
const file = queue.shift();
|
|
2153
2409
|
if (sources.has(file)) continue;
|
|
2154
2410
|
const parsed = parseFile(file, diagnostics, directives);
|
|
2155
|
-
if (!parsed) return { modules: [], diagnostics };
|
|
2411
|
+
if (!parsed) return { files: [], modules: [], diagnostics };
|
|
2156
2412
|
const { program: program3, text } = parsed;
|
|
2157
|
-
const scopes = (0,
|
|
2413
|
+
const scopes = (0, import_parser7.analyzeScopes)(program3, { noLib: config?.noLib });
|
|
2158
2414
|
for (const d of scopes.diagnostics) {
|
|
2159
2415
|
diagnostics.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "scope" });
|
|
2160
2416
|
}
|
|
2161
2417
|
sources.set(file, { file, name: nameOf(file), text, program: program3, scopes });
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2418
|
+
const dynamic = (0, import_parser7.importExpressions)(program3).flatMap((node) => node.source.type === "StringLiteral" ? [node.source.value] : []);
|
|
2419
|
+
for (const specifier of [...importedSpecifiers(program3), ...dynamic]) {
|
|
2420
|
+
const target2 = (0, import_parser7.resolveModulePath)(file, specifier, config);
|
|
2421
|
+
if (target2 && !target2.endsWith(".d.tilua")) queue.push(target2);
|
|
2165
2422
|
}
|
|
2166
2423
|
}
|
|
2167
2424
|
const analysis = analyzeModules([...sources.values()], config);
|
|
@@ -2173,6 +2430,9 @@ async function bundle(options) {
|
|
|
2173
2430
|
const names = Names.from(...[...sources.values()].map((s) => s.program));
|
|
2174
2431
|
const G = names.fresh("G");
|
|
2175
2432
|
const requireExpression = member(identifier(G), "require");
|
|
2433
|
+
const importExpression = member(identifier(G), "import");
|
|
2434
|
+
const mode = options.output ?? config?.output ?? "single";
|
|
2435
|
+
const target = options.target ?? config?.target ?? "luau";
|
|
2176
2436
|
const modules = /* @__PURE__ */ new Map();
|
|
2177
2437
|
const origins = /* @__PURE__ */ new WeakMap();
|
|
2178
2438
|
const pending = [entry];
|
|
@@ -2181,104 +2441,228 @@ async function bundle(options) {
|
|
|
2181
2441
|
if (modules.has(file)) continue;
|
|
2182
2442
|
const source = sources.get(file);
|
|
2183
2443
|
const shown = (0, import_node_path2.relative)(root, file).replace(/\\/g, "/");
|
|
2184
|
-
const
|
|
2444
|
+
const imports = /* @__PURE__ */ new Set();
|
|
2445
|
+
const dynamicImports = /* @__PURE__ */ new Set();
|
|
2446
|
+
const lowered2 = lower(source.program, source.scopes, {
|
|
2185
2447
|
names,
|
|
2186
2448
|
types: analysis.types.get(file),
|
|
2187
2449
|
lowerings,
|
|
2188
|
-
target
|
|
2450
|
+
target,
|
|
2451
|
+
noLib: config?.noLib,
|
|
2189
2452
|
source: source.text,
|
|
2190
2453
|
file: shown,
|
|
2191
2454
|
module: {
|
|
2192
2455
|
name: source.name,
|
|
2193
2456
|
require: requireExpression,
|
|
2194
|
-
|
|
2457
|
+
import: importExpression,
|
|
2458
|
+
// One file has one line map. Several have one each, and a
|
|
2459
|
+
// module reads the one of the file it is in.
|
|
2460
|
+
lines: mode === "single" ? `${G}.lines` : `${G}.maps[${(0, import_parser7.luauString)(source.name)}]`,
|
|
2195
2461
|
fail: `${G}.fail`,
|
|
2196
2462
|
arrays: `${G}.arrays`,
|
|
2197
|
-
resolve: (specifier) => {
|
|
2198
|
-
const
|
|
2199
|
-
if (!
|
|
2200
|
-
pending.push(
|
|
2201
|
-
|
|
2463
|
+
resolve: (specifier, dynamic) => {
|
|
2464
|
+
const found = (0, import_parser7.resolveModulePath)(file, specifier, config);
|
|
2465
|
+
if (!found || found.endsWith(".d.tilua") || !sources.has(found)) return void 0;
|
|
2466
|
+
pending.push(found);
|
|
2467
|
+
const name = sources.get(found).name;
|
|
2468
|
+
(dynamic ? dynamicImports : imports).add(name);
|
|
2469
|
+
return name;
|
|
2202
2470
|
}
|
|
2203
2471
|
}
|
|
2204
2472
|
});
|
|
2205
|
-
for (const d of
|
|
2206
|
-
for (const statement of allStatements(
|
|
2207
|
-
const line =
|
|
2473
|
+
for (const d of lowered2.diagnostics) diagnostics.push({ file, ...d, category: "module" });
|
|
2474
|
+
for (const statement of allStatements(lowered2.statements)) {
|
|
2475
|
+
const line = lowered2.origins.get(statement);
|
|
2208
2476
|
if (line !== void 0) origins.set(statement, { file: shown, line });
|
|
2209
2477
|
}
|
|
2210
2478
|
modules.set(file, {
|
|
2211
2479
|
name: source.name,
|
|
2212
|
-
statements:
|
|
2213
|
-
exportsName:
|
|
2214
|
-
info:
|
|
2215
|
-
usesScriptArgs:
|
|
2480
|
+
statements: lowered2.statements,
|
|
2481
|
+
exportsName: lowered2.exportsName,
|
|
2482
|
+
info: lowered2.module,
|
|
2483
|
+
usesScriptArgs: lowered2.usesScriptArgs,
|
|
2484
|
+
imports,
|
|
2485
|
+
dynamicImports
|
|
2216
2486
|
});
|
|
2217
2487
|
}
|
|
2218
2488
|
for (const [file, fileDirectives] of directives) {
|
|
2219
2489
|
const semantic = diagnostics.filter((d) => d.file === file && (d.category === "scope" || d.category === "type"));
|
|
2220
|
-
const { kept, unusedExpectErrors } = (0,
|
|
2490
|
+
const { kept, unusedExpectErrors } = (0, import_parser7.applyDirectives)(fileDirectives, semantic, (d) => d.line);
|
|
2221
2491
|
const suppressed = new Set(semantic.filter((d) => !kept.includes(d)));
|
|
2222
2492
|
const remaining = diagnostics.filter((d) => !suppressed.has(d));
|
|
2223
2493
|
diagnostics.length = 0;
|
|
2224
2494
|
diagnostics.push(...remaining);
|
|
2225
2495
|
if (options.typeCheck === false) continue;
|
|
2226
2496
|
for (const d of unusedExpectErrors) {
|
|
2227
|
-
diagnostics.push({ file, message:
|
|
2497
|
+
diagnostics.push({ file, message: import_parser7.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
|
|
2228
2498
|
}
|
|
2229
2499
|
}
|
|
2230
2500
|
const moduleProblems = new Set(diagnostics.filter((d) => d.category === "module").map((d) => `${d.file}:${d.line}:${d.column}:${d.message}`));
|
|
2231
2501
|
const reported = diagnostics.filter((d) => d.category !== "type" || !moduleProblems.has(`${d.file}:${d.line}:${d.column}:${d.message}`));
|
|
2232
2502
|
diagnostics.length = 0;
|
|
2233
2503
|
diagnostics.push(...reported);
|
|
2234
|
-
const
|
|
2504
|
+
const lowered = [...modules.values()];
|
|
2505
|
+
const moduleNames = lowered.map((m) => m.name);
|
|
2235
2506
|
if (diagnostics.some((d) => d.category === "syntax" || d.category === "scope" || d.category === "module")) {
|
|
2236
|
-
return { modules: moduleNames, diagnostics };
|
|
2237
|
-
}
|
|
2238
|
-
const
|
|
2507
|
+
return { files: [], modules: moduleNames, diagnostics };
|
|
2508
|
+
}
|
|
2509
|
+
const several = mode !== "single";
|
|
2510
|
+
const layout = layoutOf(mode, modules.get(entry), lowered, options.outFile);
|
|
2511
|
+
const usesScriptArgs = lowered.some((m) => m.usesScriptArgs);
|
|
2512
|
+
const elsewhere = /* @__PURE__ */ new Map();
|
|
2513
|
+
for (const file of layout.slice(1)) {
|
|
2514
|
+
for (const module2 of file.modules) elsewhere.set(module2.name, loadPath(layout[0].path, file.path, target));
|
|
2515
|
+
}
|
|
2516
|
+
const program2 = (0, import_luau_parser2.parse)(runtime(G, {
|
|
2517
|
+
several,
|
|
2518
|
+
target,
|
|
2519
|
+
files: elsewhere,
|
|
2520
|
+
imports: lowered.some((m) => m.dynamicImports.size > 0)
|
|
2521
|
+
}));
|
|
2239
2522
|
const assignment = program2.body.statements[1];
|
|
2240
|
-
if (
|
|
2523
|
+
if (usesScriptArgs) {
|
|
2241
2524
|
program2.body.statements.unshift(local(["scriptArgs"], [table([{ type: "TableFieldPositional", value: { type: "VarargExpression" } }])]));
|
|
2242
2525
|
program2.body.statements.push(...(0, import_luau_parser2.parse)(`${G}.arrays[scriptArgs] = true`).body.statements);
|
|
2526
|
+
if (several) program2.body.statements.push(...(0, import_luau_parser2.parse)(`${G}.scriptArgs = scriptArgs`).body.statements);
|
|
2243
2527
|
}
|
|
2244
2528
|
const modulesTable = assignment.type === "AssignmentStatement" && assignment.values[0].type === "TableExpression" ? assignment.values[0].fields.find((f) => f.type === "TableFieldNamed" && f.name.name === "modules")?.value : void 0;
|
|
2245
2529
|
if (!modulesTable) throw new Error("@tilua/compiler: the bundle runtime has no modules table");
|
|
2246
|
-
for (const module2 of modules
|
|
2247
|
-
|
|
2248
|
-
const fields = [];
|
|
2249
|
-
if (exported.length) fields.push(field("names", table(exported.map((n) => field(n, boolean(true))))));
|
|
2250
|
-
if (links.length) {
|
|
2251
|
-
fields.push(field("links", table(links.map((link) => field(link.name, table([
|
|
2252
|
-
{ type: "TableFieldPositional", value: string(link.module) },
|
|
2253
|
-
...link.imported === void 0 ? [] : [{ type: "TableFieldPositional", value: string(link.imported) }]
|
|
2254
|
-
]))))));
|
|
2255
|
-
}
|
|
2256
|
-
if (stars.length) fields.push(field("stars", table(stars.map((star) => ({ type: "TableFieldPositional", value: string(star) })))));
|
|
2257
|
-
fields.push(field("load", functionExpression(functionBody([module2.exportsName], module2.statements, true))));
|
|
2258
|
-
modulesTable.fields.push({ type: "TableFieldComputed", key: string(module2.name), value: table(fields) });
|
|
2530
|
+
for (const module2 of layout[0].modules) {
|
|
2531
|
+
modulesTable.fields.push({ type: "TableFieldComputed", key: string(module2.name), value: moduleTable(module2) });
|
|
2259
2532
|
}
|
|
2260
2533
|
const ok = names.fresh("ok");
|
|
2261
|
-
const
|
|
2534
|
+
const result2 = names.fresh("result");
|
|
2262
2535
|
const entryStatements = (0, import_luau_parser2.parse)(`
|
|
2263
|
-
local ${ok}, ${
|
|
2264
|
-
return ${G}.require(${(0,
|
|
2536
|
+
local ${ok}, ${result2} = xpcall(function()
|
|
2537
|
+
return ${G}.require(${(0, import_parser7.luauString)(sources.get(entry).name)})
|
|
2265
2538
|
end, ${G}.fail)
|
|
2266
2539
|
if not ${ok} then
|
|
2267
|
-
error(${
|
|
2540
|
+
error(${result2}, 0)
|
|
2268
2541
|
end
|
|
2269
|
-
${modules.get(entry).info.exports ? `return ${
|
|
2542
|
+
${modules.get(entry).info.exports ? `return ${result2}` : ""}
|
|
2270
2543
|
`).body.statements;
|
|
2271
2544
|
program2.body.statements.push(...entryStatements);
|
|
2272
2545
|
const map = buildLineMap(program2, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(program2)), origins);
|
|
2273
|
-
if (map.size) {
|
|
2274
|
-
|
|
2546
|
+
if (map.size || several) {
|
|
2547
|
+
const mapStatements = [assign(
|
|
2275
2548
|
[member(identifier(G), "lines")],
|
|
2276
2549
|
[(0, import_luau_parser2.parseExpressionFromSource)(lineMapSource(map))]
|
|
2277
|
-
)
|
|
2550
|
+
)];
|
|
2551
|
+
if (several) mapStatements.push(...mapRegistration(G, layout[0], `${G}.lines`));
|
|
2552
|
+
program2.body.statements.splice(program2.body.statements.length - entryStatements.length, 0, ...mapStatements);
|
|
2553
|
+
}
|
|
2554
|
+
const files = [{ path: layout[0].path, code: (0, import_luau_parser2.print)(program2) + "\n", modules: layout[0].modules.map((m) => m.name) }];
|
|
2555
|
+
for (const file of layout.slice(1)) {
|
|
2556
|
+
const body = [];
|
|
2557
|
+
if (file.modules.some((m) => m.usesScriptArgs)) body.push(...(0, import_luau_parser2.parse)(`local scriptArgs = ${G}.scriptArgs`).body.statements);
|
|
2558
|
+
for (const module2 of file.modules) {
|
|
2559
|
+
body.push(assign(
|
|
2560
|
+
[index(member(identifier(G), "modules"), string(module2.name))],
|
|
2561
|
+
[moduleTable(module2)]
|
|
2562
|
+
));
|
|
2563
|
+
}
|
|
2564
|
+
const chunk = program([returns([functionExpression(functionBody([G], body))])]);
|
|
2565
|
+
const lines = buildLineMap(chunk, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(chunk)), origins);
|
|
2566
|
+
body.push(...mapRegistration(G, file, lineMapSource(lines)));
|
|
2567
|
+
files.push({ path: file.path, code: (0, import_luau_parser2.print)(chunk) + "\n", modules: file.modules.map((m) => m.name) });
|
|
2568
|
+
}
|
|
2569
|
+
return { code: files[0].code, files, modules: moduleNames, diagnostics };
|
|
2570
|
+
}
|
|
2571
|
+
function moduleTable(module2) {
|
|
2572
|
+
const { names: exported, links, stars } = module2.info;
|
|
2573
|
+
const fields = [];
|
|
2574
|
+
if (exported.length) fields.push(field("names", table(exported.map((n) => field(n, boolean(true))))));
|
|
2575
|
+
if (links.length) {
|
|
2576
|
+
fields.push(field("links", table(links.map((link) => field(link.name, table([
|
|
2577
|
+
{ type: "TableFieldPositional", value: string(link.module) },
|
|
2578
|
+
...link.imported === void 0 ? [] : [{ type: "TableFieldPositional", value: string(link.imported) }]
|
|
2579
|
+
]))))));
|
|
2580
|
+
}
|
|
2581
|
+
if (stars.length) fields.push(field("stars", table(stars.map((star) => ({ type: "TableFieldPositional", value: string(star) })))));
|
|
2582
|
+
fields.push(field("load", functionExpression(functionBody([module2.exportsName], module2.statements, true))));
|
|
2583
|
+
return table(fields);
|
|
2584
|
+
}
|
|
2585
|
+
function layoutOf(mode, entry, modules, outFile) {
|
|
2586
|
+
const extension = outFile !== void 0 && (0, import_node_path2.extname)(outFile) || ".luau";
|
|
2587
|
+
const entryStem = outFile !== void 0 ? (0, import_node_path2.basename)(outFile, (0, import_node_path2.extname)(outFile)) : import_node_path2.posix.basename(entry.name);
|
|
2588
|
+
const others = modules.filter((m) => m !== entry);
|
|
2589
|
+
if (mode === "single") return [{ path: `${entryStem}${extension}`, modules: [entry, ...others] }];
|
|
2590
|
+
if (mode === "modules") {
|
|
2591
|
+
const pathOf = (m) => `${m.name.split("/").map((part) => part === ".." ? "__" : part).join("/")}${extension}`;
|
|
2592
|
+
return [entry, ...others].map((m) => ({ path: pathOf(m), modules: [m] }));
|
|
2593
|
+
}
|
|
2594
|
+
const byName = new Map(modules.map((m) => [m.name, m]));
|
|
2595
|
+
const reach = (root) => {
|
|
2596
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2597
|
+
const stack = [root];
|
|
2598
|
+
while (stack.length) {
|
|
2599
|
+
const name = stack.pop();
|
|
2600
|
+
if (seen.has(name)) continue;
|
|
2601
|
+
seen.add(name);
|
|
2602
|
+
stack.push(...byName.get(name)?.imports ?? []);
|
|
2603
|
+
}
|
|
2604
|
+
return seen;
|
|
2605
|
+
};
|
|
2606
|
+
const inEntry = reach(entry.name);
|
|
2607
|
+
const roots = [...new Set(modules.flatMap((m) => [...m.dynamicImports]))].filter((root) => !inEntry.has(root));
|
|
2608
|
+
const reached = new Map(roots.map((root) => [root, reach(root)]));
|
|
2609
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2610
|
+
for (const module2 of others) {
|
|
2611
|
+
if (inEntry.has(module2.name)) continue;
|
|
2612
|
+
const by = roots.filter((root) => reached.get(root).has(module2.name));
|
|
2613
|
+
const key = by.join("\n");
|
|
2614
|
+
if (!groups.has(key)) groups.set(key, { roots: by, modules: [] });
|
|
2615
|
+
groups.get(key).modules.push(module2);
|
|
2616
|
+
}
|
|
2617
|
+
const files = [{ path: `${entryStem}${extension}`, modules: modules.filter((m) => inEntry.has(m.name)) }];
|
|
2618
|
+
const taken = /* @__PURE__ */ new Set([entryStem]);
|
|
2619
|
+
for (const group of groups.values()) {
|
|
2620
|
+
const label = group.roots.length === 1 ? group.roots[0].replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "chunk" : "shared";
|
|
2621
|
+
let stem = `${entryStem}_${label}`;
|
|
2622
|
+
for (let n = 2; taken.has(stem); n++) stem = `${entryStem}_${label}_${n}`;
|
|
2623
|
+
taken.add(stem);
|
|
2624
|
+
files.push({ path: `${stem}${extension}`, modules: group.modules });
|
|
2625
|
+
}
|
|
2626
|
+
return files;
|
|
2627
|
+
}
|
|
2628
|
+
function loadPath(entryPath, file, target) {
|
|
2629
|
+
const path = import_node_path2.posix.relative(import_node_path2.posix.dirname(entryPath), file);
|
|
2630
|
+
if (target === "lua51") return path;
|
|
2631
|
+
const bare = path.slice(0, path.length - import_node_path2.posix.extname(path).length);
|
|
2632
|
+
return bare.startsWith("../") ? bare : `./${bare}`;
|
|
2633
|
+
}
|
|
2634
|
+
function mapRegistration(G, file, map) {
|
|
2635
|
+
const extension = import_node_path2.posix.extname(file.path);
|
|
2636
|
+
const parts = file.path.slice(0, file.path.length - extension.length).split(/[/.]/).filter((part) => part !== "");
|
|
2637
|
+
const entry = `${G}.sources[#${G}.sources + 1]`;
|
|
2638
|
+
return (0, import_luau_parser2.parse)([
|
|
2639
|
+
`${entry} = { { ${parts.map(import_parser7.luauString).join(", ")} }, ${map}, ${(0, import_parser7.luauString)(extension.slice(1))} }`,
|
|
2640
|
+
...file.modules.map((module2) => `${G}.maps[${(0, import_parser7.luauString)(module2.name)}] = ${G}.sources[#${G}.sources][2]`)
|
|
2641
|
+
].join("\n")).body.statements;
|
|
2642
|
+
}
|
|
2643
|
+
function loader(G, target) {
|
|
2644
|
+
if (target === "lua51") {
|
|
2645
|
+
return `
|
|
2646
|
+
-- From beside this file, wherever that is: its own path says where.
|
|
2647
|
+
local folder = ""
|
|
2648
|
+
if debug ~= nil and debug.getinfo ~= nil then
|
|
2649
|
+
local at = debug.getinfo(${G}.load, "S").source
|
|
2650
|
+
if at:sub(1, 1) == "@" then
|
|
2651
|
+
folder = at:match("^@(.*[/\\\\])") or ""
|
|
2652
|
+
end
|
|
2653
|
+
end
|
|
2654
|
+
local chunk, problem = loadfile(folder .. file)
|
|
2655
|
+
if chunk == nil then
|
|
2656
|
+
error(problem, 0)
|
|
2657
|
+
end
|
|
2658
|
+
chunk()(${G})`;
|
|
2278
2659
|
}
|
|
2279
|
-
return
|
|
2660
|
+
return `
|
|
2661
|
+
require(file)(${G})`;
|
|
2280
2662
|
}
|
|
2281
|
-
function runtime(G) {
|
|
2663
|
+
function runtime(G, options) {
|
|
2664
|
+
const { several } = options;
|
|
2665
|
+
const files = [...options.files].map(([name, file]) => `[${(0, import_parser7.luauString)(name)}] = ${(0, import_parser7.luauString)(file)}`).join(", ");
|
|
2282
2666
|
return `
|
|
2283
2667
|
local ${G}
|
|
2284
2668
|
${G} = {
|
|
@@ -2326,7 +2710,7 @@ ${G} = {
|
|
|
2326
2710
|
return message
|
|
2327
2711
|
end
|
|
2328
2712
|
local lines = ${G}.lines
|
|
2329
|
-
if lines == nil then
|
|
2713
|
+
if lines == nil ${several ? `and #${G}.sources == 0 ` : ""}then
|
|
2330
2714
|
return message .. (trace or "")
|
|
2331
2715
|
end
|
|
2332
2716
|
-- The bundle's name, as a position in a message spells it: where this
|
|
@@ -2346,6 +2730,81 @@ ${G} = {
|
|
|
2346
2730
|
chunk = info.short_src
|
|
2347
2731
|
end
|
|
2348
2732
|
end
|
|
2733
|
+
${several ? `
|
|
2734
|
+
-- A position in this file is read by this file's map. One in another
|
|
2735
|
+
-- of the bundle's files is read by that file's: the one whose name
|
|
2736
|
+
-- the position ends with, by the most folders where several do. A
|
|
2737
|
+
-- message says nothing about which file it starts in, so nothing is
|
|
2738
|
+
-- guessed from it here.
|
|
2739
|
+
local prefix = nil
|
|
2740
|
+
if chunk ~= nil then
|
|
2741
|
+
prefix = chunk:gsub("%p", "%%%0")
|
|
2742
|
+
end
|
|
2743
|
+
local function place(line)
|
|
2744
|
+
local origin = lines ~= nil and lines[tonumber(line)] or nil
|
|
2745
|
+
if origin == nil then
|
|
2746
|
+
return nil
|
|
2747
|
+
end
|
|
2748
|
+
return origin[1] .. ":" .. tostring(origin[2])
|
|
2749
|
+
end
|
|
2750
|
+
local function elsewhere(at, line)
|
|
2751
|
+
-- Already a place in the project.
|
|
2752
|
+
if at:sub(-6) == ".tilua" then
|
|
2753
|
+
return nil
|
|
2754
|
+
end
|
|
2755
|
+
local parts = {}
|
|
2756
|
+
for part in at:gmatch("[^/\\\\%.]+") do
|
|
2757
|
+
parts[#parts + 1] = part
|
|
2758
|
+
end
|
|
2759
|
+
local map, most, tied = nil, 0, false
|
|
2760
|
+
for _, source in ipairs(${G}.sources) do
|
|
2761
|
+
local names = source[1]
|
|
2762
|
+
local last = #parts
|
|
2763
|
+
if parts[last] == source[3] then
|
|
2764
|
+
last = last - 1
|
|
2765
|
+
end
|
|
2766
|
+
local same = 0
|
|
2767
|
+
while same < #names and same < last and names[#names - same] == parts[last - same] do
|
|
2768
|
+
same = same + 1
|
|
2769
|
+
end
|
|
2770
|
+
if same > most then
|
|
2771
|
+
map, most, tied = source[2], same, false
|
|
2772
|
+
elseif same == most then
|
|
2773
|
+
tied = true
|
|
2774
|
+
end
|
|
2775
|
+
end
|
|
2776
|
+
-- Two files it could as well be: a wrong place is worse than none.
|
|
2777
|
+
local origin = map ~= nil and not tied and map[tonumber(line)] or nil
|
|
2778
|
+
if origin == nil then
|
|
2779
|
+
return nil
|
|
2780
|
+
end
|
|
2781
|
+
return origin[1] .. ":" .. tostring(origin[2])
|
|
2782
|
+
end
|
|
2783
|
+
local mapped = message
|
|
2784
|
+
if prefix ~= nil then
|
|
2785
|
+
mapped = mapped:gsub(prefix .. ":(%d+)", function(line)
|
|
2786
|
+
return place(line)
|
|
2787
|
+
end)
|
|
2788
|
+
end
|
|
2789
|
+
mapped = mapped:gsub("(%S+):(%d+)", elsewhere)
|
|
2790
|
+
local frames = {}
|
|
2791
|
+
for frame in (trace or ""):gmatch("[^\\n]+") do
|
|
2792
|
+
local at = nil
|
|
2793
|
+
if prefix ~= nil then
|
|
2794
|
+
local line = frame:match(prefix .. ":(%d+)")
|
|
2795
|
+
if line ~= nil then
|
|
2796
|
+
at = place(line)
|
|
2797
|
+
end
|
|
2798
|
+
end
|
|
2799
|
+
if at == nil then
|
|
2800
|
+
for source, line in frame:gmatch("(%S+):(%d+)") do
|
|
2801
|
+
at = elsewhere(source, line)
|
|
2802
|
+
if at ~= nil then
|
|
2803
|
+
break
|
|
2804
|
+
end
|
|
2805
|
+
end
|
|
2806
|
+
end
|
|
2807
|
+
if at ~= nil then` : `
|
|
2349
2808
|
if chunk == nil then
|
|
2350
2809
|
chunk = message:match("^(.-):%d+:")
|
|
2351
2810
|
end
|
|
@@ -2367,8 +2826,9 @@ ${G} = {
|
|
|
2367
2826
|
for frame in (trace or ""):gmatch("[^\\n]+") do
|
|
2368
2827
|
local line = frame:match(prefix .. ":(%d+)")
|
|
2369
2828
|
local at = line ~= nil and place(line) or nil
|
|
2370
|
-
if at ~= nil then
|
|
2371
|
-
|
|
2829
|
+
if at ~= nil then`}
|
|
2830
|
+
-- Luau writes \`function name\`; Lua 5.1, \`function 'name'\`.
|
|
2831
|
+
local name = frame:match("function ([%w_.:]+)") or frame:match("function '([^']+)'")
|
|
2372
2832
|
frames[#frames + 1] = " at " .. at .. (name ~= nil and (" (" .. name .. ")") or "")
|
|
2373
2833
|
end
|
|
2374
2834
|
end
|
|
@@ -2377,13 +2837,36 @@ ${G} = {
|
|
|
2377
2837
|
end
|
|
2378
2838
|
return mapped .. "\\n" .. table.concat(frames, "\\n")
|
|
2379
2839
|
end,
|
|
2380
|
-
records = {}
|
|
2840
|
+
records = {},${several ? `
|
|
2841
|
+
-- The file each module outside this one is in, as \`load\` is handed it.
|
|
2842
|
+
files = { ${files} },
|
|
2843
|
+
loaded = {},
|
|
2844
|
+
-- Every file's line map: by the module, and by the file's name.
|
|
2845
|
+
maps = {},
|
|
2846
|
+
sources = {},
|
|
2847
|
+
-- Another of the bundle's files: a function that adds its modules here.
|
|
2848
|
+
load = function(file)
|
|
2849
|
+
if ${G}.loaded[file] then
|
|
2850
|
+
return
|
|
2851
|
+
end
|
|
2852
|
+
${G}.loaded[file] = true${loader(G, options.target)}
|
|
2853
|
+
end,` : ""}${options.imports ? `
|
|
2854
|
+
-- \`import("./x")\`: the module's exports, asked for where the code runs
|
|
2855
|
+
-- rather than before its module does. \`require\` does the rest \u2014 and
|
|
2856
|
+
-- loads the file the module is in, if it is not this one.
|
|
2857
|
+
import = function(name)
|
|
2858
|
+
return ${G}.require(name)
|
|
2859
|
+
end,` : ""}
|
|
2381
2860
|
require = function(name)
|
|
2382
2861
|
local record = ${G}.records[name]
|
|
2383
2862
|
if record ~= nil then
|
|
2384
2863
|
return record.exports
|
|
2385
2864
|
end
|
|
2386
|
-
local module = ${G}.modules[name]
|
|
2865
|
+
local module = ${G}.modules[name]${several ? `
|
|
2866
|
+
if module == nil then
|
|
2867
|
+
${G}.load(${G}.files[name])
|
|
2868
|
+
module = ${G}.modules[name]
|
|
2869
|
+
end` : ""}
|
|
2387
2870
|
local initialized = {}
|
|
2388
2871
|
local exports = setmetatable({}, {
|
|
2389
2872
|
__index = function(_, key)
|
|
@@ -2402,7 +2885,7 @@ ${G} = {
|
|
|
2402
2885
|
return source[link[2]]
|
|
2403
2886
|
end
|
|
2404
2887
|
if module.stars ~= nil and key ~= "default" then
|
|
2405
|
-
for _, star in module.stars do
|
|
2888
|
+
for _, star in ipairs(module.stars) do
|
|
2406
2889
|
local value = ${G}.require(star)[key]
|
|
2407
2890
|
if value ~= nil then
|
|
2408
2891
|
return value
|
|
@@ -2427,7 +2910,7 @@ ${G} = {
|
|
|
2427
2910
|
}
|
|
2428
2911
|
function stampOf(file) {
|
|
2429
2912
|
try {
|
|
2430
|
-
const at = (0,
|
|
2913
|
+
const at = (0, import_node_fs3.statSync)(file);
|
|
2431
2914
|
return `${at.size}:${at.mtimeMs}`;
|
|
2432
2915
|
} catch {
|
|
2433
2916
|
return void 0;
|
|
@@ -2443,19 +2926,19 @@ function parseFile(file, diagnostics, directives) {
|
|
|
2443
2926
|
}
|
|
2444
2927
|
let text;
|
|
2445
2928
|
try {
|
|
2446
|
-
text = (0,
|
|
2929
|
+
text = (0, import_node_fs3.readFileSync)(file, "utf8");
|
|
2447
2930
|
} catch {
|
|
2448
2931
|
diagnostics.push({ file, message: "Cannot read file", line: 1, column: 1, category: "module" });
|
|
2449
2932
|
return void 0;
|
|
2450
2933
|
}
|
|
2451
2934
|
try {
|
|
2452
|
-
const program2 = (0,
|
|
2453
|
-
const found = (0,
|
|
2935
|
+
const program2 = (0, import_parser7.parse)(text);
|
|
2936
|
+
const found = (0, import_parser7.directivesOf)(text);
|
|
2454
2937
|
directives.set(file, found);
|
|
2455
2938
|
if (stamp !== void 0) parsedFiles.set(file, { stamp, program: program2, text, directives: found });
|
|
2456
2939
|
return { program: program2, text };
|
|
2457
2940
|
} catch (error) {
|
|
2458
|
-
if (error instanceof
|
|
2941
|
+
if (error instanceof import_parser7.ParseError || error instanceof import_parser7.LexError) {
|
|
2459
2942
|
const { line, column } = error;
|
|
2460
2943
|
diagnostics.push({ file, message: error.message, line, column, category: "syntax" });
|
|
2461
2944
|
return void 0;
|
|
@@ -2470,14 +2953,14 @@ var libraryCache = /* @__PURE__ */ new Map();
|
|
|
2470
2953
|
function parsedLibrary(file) {
|
|
2471
2954
|
let stamp;
|
|
2472
2955
|
try {
|
|
2473
|
-
const at = (0,
|
|
2956
|
+
const at = (0, import_node_fs3.statSync)(file);
|
|
2474
2957
|
stamp = `${at.size}:${at.mtimeMs}`;
|
|
2475
2958
|
} catch {
|
|
2476
2959
|
stamp = "";
|
|
2477
2960
|
}
|
|
2478
2961
|
const cached = libraryCache.get(file);
|
|
2479
2962
|
if (cached && cached.stamp === stamp) return cached.program;
|
|
2480
|
-
const program2 = (0,
|
|
2963
|
+
const program2 = (0, import_parser7.parse)((0, import_node_fs3.readFileSync)(file, "utf8"));
|
|
2481
2964
|
libraryCache.set(file, { stamp, program: program2 });
|
|
2482
2965
|
return program2;
|
|
2483
2966
|
}
|
|
@@ -2485,9 +2968,11 @@ var moduleAnalyses = /* @__PURE__ */ new Map();
|
|
|
2485
2968
|
function analyzeModules(modules, config) {
|
|
2486
2969
|
const out = [];
|
|
2487
2970
|
const analyses = /* @__PURE__ */ new Map();
|
|
2488
|
-
const libraries = config ? (0,
|
|
2971
|
+
const libraries = config ? (0, import_parser7.resolveTypeLibraries)(config) : { files: [], lowerings: [], problems: [] };
|
|
2489
2972
|
for (const p of libraries.problems) out.push({ file: p.file, message: p.message, line: p.line ?? 1, column: p.column ?? 1, category: "config" });
|
|
2490
2973
|
const libs = libraries.files.map(parsedLibrary);
|
|
2974
|
+
const noLib = config?.noLib ?? false;
|
|
2975
|
+
const checked = libs.length > 0 || !noLib;
|
|
2491
2976
|
const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
|
|
2492
2977
|
const byFile = new Map(modules.map((m) => [m.file, m]));
|
|
2493
2978
|
const exportsCache = /* @__PURE__ */ new Map();
|
|
@@ -2500,49 +2985,50 @@ function analyzeModules(modules, config) {
|
|
|
2500
2985
|
const done = analyzed.get(file);
|
|
2501
2986
|
if (done) return done;
|
|
2502
2987
|
const remembered = moduleAnalyses.get(file);
|
|
2503
|
-
if (remembered && remembered.setting === settingFor && libs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === libs[i]) && remembered.program === program2 && [...remembered.read].every(([
|
|
2988
|
+
if (remembered && remembered.setting === settingFor && libs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === libs[i]) && remembered.program === program2 && [...remembered.read].every(([read3, stamp]) => stampOf(read3) === stamp)) {
|
|
2504
2989
|
analyzed.set(file, remembered.result);
|
|
2505
2990
|
readWhile.set(file, remembered.read);
|
|
2506
2991
|
return remembered.result;
|
|
2507
2992
|
}
|
|
2508
|
-
const scopes = (0,
|
|
2993
|
+
const scopes = (0, import_parser7.analyzeScopes)(program2, { builtinGlobals: globals, reportUndeclared: checked, noLib });
|
|
2509
2994
|
const said = [];
|
|
2510
2995
|
for (const d of scopes.diagnostics) {
|
|
2511
2996
|
if (d.kind !== "undeclared") continue;
|
|
2512
2997
|
said.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "type" });
|
|
2513
2998
|
}
|
|
2514
2999
|
const before = partials;
|
|
2515
|
-
const types = (0,
|
|
3000
|
+
const types = (0, import_parser7.analyzeTypes)(program2, scopes, {
|
|
2516
3001
|
libs,
|
|
2517
3002
|
resolveModule: resolverFor(file),
|
|
2518
|
-
|
|
3003
|
+
noLib,
|
|
3004
|
+
reportUnknownTypes: checked
|
|
2519
3005
|
});
|
|
2520
3006
|
for (const d of types.diagnostics) {
|
|
2521
3007
|
const at = d.node;
|
|
2522
3008
|
said.push({ file, message: d.message, line: at.line.start, column: at.column.start, category: "type" });
|
|
2523
3009
|
}
|
|
2524
|
-
const
|
|
3010
|
+
const result2 = { types, diagnostics: said };
|
|
2525
3011
|
if (partials === before) {
|
|
2526
|
-
analyzed.set(file,
|
|
2527
|
-
const
|
|
3012
|
+
analyzed.set(file, result2);
|
|
3013
|
+
const read3 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
2528
3014
|
const own = stampOf(file);
|
|
2529
|
-
if (own !== void 0)
|
|
2530
|
-
readWhile.set(file,
|
|
2531
|
-
if ([...
|
|
2532
|
-
moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program: program2, read:
|
|
3015
|
+
if (own !== void 0) read3.set(file, own);
|
|
3016
|
+
readWhile.set(file, read3);
|
|
3017
|
+
if ([...read3.values()].every((stamp) => stamp !== void 0)) {
|
|
3018
|
+
moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program: program2, read: read3, result: result2 });
|
|
2533
3019
|
}
|
|
2534
3020
|
}
|
|
2535
|
-
return
|
|
3021
|
+
return result2;
|
|
2536
3022
|
};
|
|
2537
3023
|
const resolverFor = (file) => (specifier) => {
|
|
2538
|
-
const target = (0,
|
|
3024
|
+
const target = (0, import_parser7.resolveModulePath)(file, specifier, config);
|
|
2539
3025
|
if (!target) return void 0;
|
|
2540
3026
|
const noteRead = () => {
|
|
2541
|
-
const
|
|
2542
|
-
readWhile.set(file,
|
|
3027
|
+
const read3 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
3028
|
+
readWhile.set(file, read3);
|
|
2543
3029
|
const stamp = stampOf(target);
|
|
2544
|
-
if (stamp !== void 0)
|
|
2545
|
-
for (const [below, belowStamp] of readWhile.get(target) ?? [])
|
|
3030
|
+
if (stamp !== void 0) read3.set(target, stamp);
|
|
3031
|
+
for (const [below, belowStamp] of readWhile.get(target) ?? []) read3.set(below, belowStamp);
|
|
2546
3032
|
};
|
|
2547
3033
|
if (inProgress.has(target)) {
|
|
2548
3034
|
partials++;
|
|
@@ -2556,16 +3042,16 @@ function analyzeModules(modules, config) {
|
|
|
2556
3042
|
let program2 = byFile.get(target)?.program;
|
|
2557
3043
|
if (!program2) {
|
|
2558
3044
|
try {
|
|
2559
|
-
program2 = (0,
|
|
3045
|
+
program2 = (0, import_parser7.parse)((0, import_node_fs3.readFileSync)(target, "utf8"));
|
|
2560
3046
|
} catch {
|
|
2561
3047
|
return void 0;
|
|
2562
3048
|
}
|
|
2563
3049
|
}
|
|
2564
3050
|
inProgress.add(target);
|
|
2565
3051
|
try {
|
|
2566
|
-
const scopes = (0,
|
|
2567
|
-
const types = byFile.has(target) ? analyzeModule(target, program2).types : (0,
|
|
2568
|
-
const exports2 = (0,
|
|
3052
|
+
const scopes = (0, import_parser7.analyzeScopes)(program2, { builtinGlobals: globals, noLib });
|
|
3053
|
+
const types = byFile.has(target) ? analyzeModule(target, program2).types : (0, import_parser7.analyzeTypes)(program2, scopes, { libs, noLib, resolveModule: resolverFor(target), diagnostics: false });
|
|
3054
|
+
const exports2 = (0, import_parser7.moduleExports)(program2, scopes, types, resolverFor(target));
|
|
2569
3055
|
exportsCache.set(target, exports2);
|
|
2570
3056
|
noteRead();
|
|
2571
3057
|
return exports2;
|
|
@@ -2598,37 +3084,39 @@ function allStatements(statements) {
|
|
|
2598
3084
|
}
|
|
2599
3085
|
|
|
2600
3086
|
// src/compile.ts
|
|
2601
|
-
var
|
|
3087
|
+
var import_node_fs4 = require("fs");
|
|
2602
3088
|
var import_node_path3 = require("path");
|
|
2603
|
-
var
|
|
3089
|
+
var import_parser8 = require("@tilua/parser");
|
|
2604
3090
|
var import_luau_parser3 = require("luau-parser");
|
|
2605
3091
|
async function compile(source, config) {
|
|
2606
3092
|
let program2;
|
|
2607
3093
|
try {
|
|
2608
|
-
program2 = (0,
|
|
3094
|
+
program2 = (0, import_parser8.parse)(source);
|
|
2609
3095
|
} catch (error) {
|
|
2610
|
-
if (error instanceof
|
|
3096
|
+
if (error instanceof import_parser8.ParseError || error instanceof import_parser8.LexError) {
|
|
2611
3097
|
const { line, column } = error;
|
|
2612
3098
|
return { diagnostics: [{ message: error.message, line, column }] };
|
|
2613
3099
|
}
|
|
2614
3100
|
throw error;
|
|
2615
3101
|
}
|
|
2616
|
-
const
|
|
3102
|
+
const { config: resolved, problems } = resolveConfig(config, (0, import_node_path3.resolve)("main.tilua"));
|
|
3103
|
+
const noLib = resolved?.noLib ?? false;
|
|
3104
|
+
const scopes = (0, import_parser8.analyzeScopes)(program2, { noLib });
|
|
2617
3105
|
const diagnostics = scopes.diagnostics.map((d) => ({
|
|
2618
3106
|
message: d.message,
|
|
2619
3107
|
line: d.node.line.start,
|
|
2620
3108
|
column: d.node.column.start
|
|
2621
3109
|
}));
|
|
2622
|
-
const
|
|
2623
|
-
const libraries = resolved ? (0, import_parser7.resolveTypeLibraries)(resolved) : { files: [], lowerings: [], problems: [] };
|
|
3110
|
+
const libraries = resolved ? (0, import_parser8.resolveTypeLibraries)(resolved) : { files: [], lowerings: [], problems: [] };
|
|
2624
3111
|
const { lowerings, problems: loweringProblems } = await loadLowerings(libraries.lowerings);
|
|
2625
3112
|
for (const p of [...problems, ...libraries.problems]) {
|
|
2626
3113
|
diagnostics.push({ message: p.message, line: p.line ?? 1, column: p.column ?? 1 });
|
|
2627
3114
|
}
|
|
2628
3115
|
for (const p of loweringProblems) diagnostics.push({ message: p.message, line: 1, column: 1 });
|
|
2629
|
-
const libs = libraries.files.map((file) => (0,
|
|
3116
|
+
const libs = libraries.files.map((file) => (0, import_parser8.parse)((0, import_node_fs4.readFileSync)(file, "utf8")));
|
|
2630
3117
|
const lowered = lower(program2, scopes, {
|
|
2631
|
-
types: (0,
|
|
3118
|
+
types: (0, import_parser8.analyzeTypes)(program2, scopes, { libs, noLib, diagnostics: false }),
|
|
3119
|
+
noLib,
|
|
2632
3120
|
lowerings,
|
|
2633
3121
|
target: resolved?.target,
|
|
2634
3122
|
source
|