@tilua/compiler 4.0.0 → 5.0.1
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 +101 -10
- package/dist/{chunk-VZ7XC4BG.js → chunk-Y7QGXLCV.js} +643 -127
- package/dist/cli.cjs +709 -171
- package/dist/cli.js +32 -9
- package/dist/index.cjs +665 -148
- 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 +36 -4
- package/runtime/console.luau +227 -0
- package/runtime/result.luau +122 -0
|
@@ -172,6 +172,77 @@ var plugin = {
|
|
|
172
172
|
};
|
|
173
173
|
var languageLowering = { plugin, from: "tilua" };
|
|
174
174
|
|
|
175
|
+
// src/standard.ts
|
|
176
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
177
|
+
import { isFunctionType, luauString as luauString2 } from "@tilua/parser";
|
|
178
|
+
var read2 = (name) => readFileSync2(new URL(`../runtime/${name}`, import.meta.url), "utf8");
|
|
179
|
+
var console = read2("console.luau");
|
|
180
|
+
var result = read2("result.luau");
|
|
181
|
+
var place = (at) => luauString2(at.file === void 0 ? `line ${at.line}` : `${at.file}:${at.line}`);
|
|
182
|
+
var tag = (at) => luauString2(`[${at.file === void 0 ? `line ${at.line}` : `${at.file.replace(/\.tilua$/, "")}:${at.line}`}]:`);
|
|
183
|
+
function consoleMeta(args) {
|
|
184
|
+
const fields = [];
|
|
185
|
+
args.forEach((argument, index2) => {
|
|
186
|
+
if (argument.spread || argument.typeText === void 0 || !isFunctionType(argument.type)) return;
|
|
187
|
+
const written = argument.declaration ?? argument.source;
|
|
188
|
+
fields.push(`[${index2 + 1}] = { ${luauString2(argument.typeText)}, ${luauString2(written)} }`);
|
|
189
|
+
});
|
|
190
|
+
return fields.length ? `{ ${fields.join(", ")} }` : "nil";
|
|
191
|
+
}
|
|
192
|
+
var CONSOLE_METHODS = /* @__PURE__ */ new Set(["log", "warn", "error"]);
|
|
193
|
+
var REPLACED = /* @__PURE__ */ new Map([
|
|
194
|
+
["pcall", "pcall"],
|
|
195
|
+
["xpcall", "xpcall"],
|
|
196
|
+
["ypcall", "pcall"],
|
|
197
|
+
["coroutine.resume", "resume"],
|
|
198
|
+
["string.find", "find"],
|
|
199
|
+
["string.match", "match"],
|
|
200
|
+
["string.gsub", "gsub"],
|
|
201
|
+
["string.byte", "byte"],
|
|
202
|
+
["math.modf", "modf"],
|
|
203
|
+
["math.frexp", "frexp"],
|
|
204
|
+
["next", "next"],
|
|
205
|
+
["select", "select"],
|
|
206
|
+
["unpack", "unpack"],
|
|
207
|
+
["table.unpack", "unpack"],
|
|
208
|
+
["loadstring", "loadstring"],
|
|
209
|
+
["pairs", "pairs"],
|
|
210
|
+
["ipairs", "ipairs"],
|
|
211
|
+
["string.gmatch", "gmatch"]
|
|
212
|
+
]);
|
|
213
|
+
var plugin2 = {
|
|
214
|
+
runtime: { console, result },
|
|
215
|
+
methodCall({ method, receiverGlobal, at, arguments: args, use }) {
|
|
216
|
+
if (receiverGlobal === "console" && CONSOLE_METHODS.has(method)) {
|
|
217
|
+
return {
|
|
218
|
+
callee: `${use("console")}.${method}`,
|
|
219
|
+
prepend: [tag(at), consoleMeta(args)],
|
|
220
|
+
passReceiver: false
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return void 0;
|
|
224
|
+
},
|
|
225
|
+
// `print(x)` and `warn(x)` are left exactly as written: the place a line
|
|
226
|
+
// came from is what `console:log` / `console:warn` are for. `error` has to
|
|
227
|
+
// put the place inside the message it raises, so it goes through the
|
|
228
|
+
// runtime.
|
|
229
|
+
globalCall({ name, at, use }) {
|
|
230
|
+
if (name === "error") return { callee: `${use("console")}.raise`, prepend: [place(at)] };
|
|
231
|
+
const replaced = REPLACED.get(name);
|
|
232
|
+
if (replaced) return { callee: `${use("result")}.${replaced}` };
|
|
233
|
+
return void 0;
|
|
234
|
+
},
|
|
235
|
+
// Read as a value there is no call site to write down; the runtime finds
|
|
236
|
+
// one on the stack.
|
|
237
|
+
globalValue({ name, use }) {
|
|
238
|
+
if (name === "error") return `${use("console")}.raiseValue`;
|
|
239
|
+
const replaced = REPLACED.get(name);
|
|
240
|
+
if (replaced) return `${use("result")}.${replaced}`;
|
|
241
|
+
return void 0;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
var standardLowering = { plugin: plugin2, from: "tilua (standard library)" };
|
|
245
|
+
|
|
175
246
|
// src/lower.ts
|
|
176
247
|
function lower(program2, scopes, options = {}) {
|
|
177
248
|
return new Lowerer(program2, scopes, options).run();
|
|
@@ -187,6 +258,18 @@ var Lowerer = class {
|
|
|
187
258
|
for (const binding of scopes.bindings.values()) {
|
|
188
259
|
if (binding.declarationNode) this.bindingByDeclaration.set(binding.declarationNode, binding);
|
|
189
260
|
}
|
|
261
|
+
const visit = (value) => {
|
|
262
|
+
if (!value || typeof value !== "object") return;
|
|
263
|
+
if (Array.isArray(value)) return void value.forEach(visit);
|
|
264
|
+
const node = value;
|
|
265
|
+
if (node.type === "VariableDeclaration" && node.name?.type === "IdentifierPattern" && node.name.name) {
|
|
266
|
+
let init = node.init;
|
|
267
|
+
while (init?.type === "ParenthesizedExpression") init = init.expression;
|
|
268
|
+
if (init?.type === "ClassExpression") this.classExpressionNames.set(init, node.name.name);
|
|
269
|
+
}
|
|
270
|
+
for (const [key, child] of Object.entries(value)) if (key !== "line" && key !== "column") visit(child);
|
|
271
|
+
};
|
|
272
|
+
visit(source.body);
|
|
190
273
|
}
|
|
191
274
|
source;
|
|
192
275
|
scopes;
|
|
@@ -208,6 +291,8 @@ var Lowerer = class {
|
|
|
208
291
|
lua51;
|
|
209
292
|
/** The source line each emitted statement came from. */
|
|
210
293
|
origins = /* @__PURE__ */ new WeakMap();
|
|
294
|
+
/** The name each class written as a value is known by. */
|
|
295
|
+
classExpressionNames = /* @__PURE__ */ new WeakMap();
|
|
211
296
|
run() {
|
|
212
297
|
const body = this.options.module ? this.module(this.options.module) : this.topLevel(this.source.body.statements);
|
|
213
298
|
const scriptArgs = this.usesScriptArgs && !this.options.module ? [local(["scriptArgs"], [this.array(table([{ type: "TableFieldPositional", value: vararg() }]))])] : [];
|
|
@@ -261,6 +346,10 @@ var Lowerer = class {
|
|
|
261
346
|
}
|
|
262
347
|
return identifier(local2);
|
|
263
348
|
}
|
|
349
|
+
/** `table.move`. Lua 5.1 has none, and gets a function that does the same. */
|
|
350
|
+
move() {
|
|
351
|
+
return this.lua51 ? this.helper("move") : member(this.builtin("table"), "move");
|
|
352
|
+
}
|
|
264
353
|
helper(kind) {
|
|
265
354
|
let name = this.helpers.get(kind);
|
|
266
355
|
if (!name) {
|
|
@@ -383,7 +472,11 @@ var Lowerer = class {
|
|
|
383
472
|
/** The language's own lowering, then each library's, in load order. A
|
|
384
473
|
* runtime is named by its lowering's place here (`loweringRuntime`). */
|
|
385
474
|
get lowerings() {
|
|
386
|
-
return this.allLowerings ??= [
|
|
475
|
+
return this.allLowerings ??= [
|
|
476
|
+
languageLowering,
|
|
477
|
+
...this.options.noLib ? [] : [standardLowering],
|
|
478
|
+
...this.options.lowerings ?? []
|
|
479
|
+
];
|
|
387
480
|
}
|
|
388
481
|
allLowerings;
|
|
389
482
|
/** What `receiver:method(...)` becomes, when Luau alone would not run it.
|
|
@@ -397,22 +490,28 @@ var Lowerer = class {
|
|
|
397
490
|
* (`console:log`), the last library loaded first. No answer leaves an
|
|
398
491
|
* ordinary Luau method call: `text:upper()` reaches Lua's own. */
|
|
399
492
|
loweredMethodCall(node) {
|
|
493
|
+
return this.loweredMethod(node.method.name, this.options.types?.methodSources.get(node), node.object, node.arguments, node.method, node);
|
|
494
|
+
}
|
|
495
|
+
/** `loweredMethodCall`, for a method named `method` of what `object`
|
|
496
|
+
* holds — called with `:` on it, or read off it with `.` and handed its
|
|
497
|
+
* `self` like any other argument. `args` are the arguments after the
|
|
498
|
+
* receiver; a read that is not called has none. */
|
|
499
|
+
loweredMethod(method, source, object2, args, name, node) {
|
|
400
500
|
const lowerings = this.lowerings;
|
|
401
|
-
const source = this.options.types?.methodSources.get(node);
|
|
402
501
|
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();
|
|
403
502
|
if (!asked.some((i) => lowerings[i].plugin.methodCall)) return void 0;
|
|
404
|
-
const receiver = this.options.types?.typeOf.get(
|
|
405
|
-
const receiverGlobal = this.globalName(
|
|
406
|
-
const info = this.lazyArguments(
|
|
503
|
+
const receiver = this.options.types?.typeOf.get(object2);
|
|
504
|
+
const receiverGlobal = this.globalName(object2);
|
|
505
|
+
const info = this.lazyArguments(args);
|
|
407
506
|
for (const i of asked) {
|
|
408
|
-
const
|
|
409
|
-
if (!
|
|
410
|
-
const answer =
|
|
411
|
-
method
|
|
507
|
+
const plugin3 = lowerings[i].plugin;
|
|
508
|
+
if (!plugin3.methodCall) continue;
|
|
509
|
+
const answer = plugin3.methodCall({
|
|
510
|
+
method,
|
|
412
511
|
receiver,
|
|
413
512
|
receiverGlobal,
|
|
414
|
-
argumentCount:
|
|
415
|
-
at: this.callSite(
|
|
513
|
+
argumentCount: args.length,
|
|
514
|
+
at: this.callSite(name),
|
|
416
515
|
get arguments() {
|
|
417
516
|
return info();
|
|
418
517
|
},
|
|
@@ -436,9 +535,9 @@ var Lowerer = class {
|
|
|
436
535
|
if (name === void 0) return void 0;
|
|
437
536
|
const info = this.lazyArguments(node.arguments);
|
|
438
537
|
for (let i = lowerings.length - 1; i >= 0; i--) {
|
|
439
|
-
const
|
|
440
|
-
if (!
|
|
441
|
-
const answer =
|
|
538
|
+
const plugin3 = lowerings[i].plugin;
|
|
539
|
+
if (!plugin3.globalCall) continue;
|
|
540
|
+
const answer = plugin3.globalCall({
|
|
442
541
|
name,
|
|
443
542
|
at: this.callSite(node.callee),
|
|
444
543
|
get arguments() {
|
|
@@ -490,6 +589,31 @@ var Lowerer = class {
|
|
|
490
589
|
const out = [];
|
|
491
590
|
const runtimes = this.runtimeDefinitions();
|
|
492
591
|
if (this.helpers.has("concat")) this.helper("array");
|
|
592
|
+
if (this.helpers.has("concat")) this.move();
|
|
593
|
+
const move = this.helpers.get("move");
|
|
594
|
+
if (move) {
|
|
595
|
+
out.push(...parseLuau(`
|
|
596
|
+
local function ${move}(from, first, last, at, to)
|
|
597
|
+
for i = first, last do
|
|
598
|
+
to[at + i - first] = from[i]
|
|
599
|
+
end
|
|
600
|
+
return to
|
|
601
|
+
end`).body.statements);
|
|
602
|
+
}
|
|
603
|
+
const each = this.helpers.get("each");
|
|
604
|
+
if (each) {
|
|
605
|
+
out.push(...parseLuau(`
|
|
606
|
+
local function ${each}(source, ...)
|
|
607
|
+
if ${this.builtin("type").name}(source) == "function" then
|
|
608
|
+
return source, ...
|
|
609
|
+
end
|
|
610
|
+
local meta = ${this.builtin("getmetatable").name}(source)
|
|
611
|
+
if meta ~= nil and meta.__iter ~= nil then
|
|
612
|
+
return meta.__iter(source)
|
|
613
|
+
end
|
|
614
|
+
return ${this.builtin("next").name}, source
|
|
615
|
+
end`).body.statements);
|
|
616
|
+
}
|
|
493
617
|
const marker = this.helpers.get("array");
|
|
494
618
|
const typeOf = this.helpers.get("typeof");
|
|
495
619
|
if (marker || typeOf) {
|
|
@@ -528,19 +652,72 @@ local function ${typeOf}(value, known)
|
|
|
528
652
|
end`).body.statements);
|
|
529
653
|
}
|
|
530
654
|
}
|
|
655
|
+
const instanceOf = this.helpers.get("instanceof");
|
|
656
|
+
if (instanceOf) {
|
|
657
|
+
out.push(...parseLuau(`
|
|
658
|
+
local function ${instanceOf}(value, class)
|
|
659
|
+
if ${this.builtin("type").name}(value) ~= "table" then
|
|
660
|
+
return false
|
|
661
|
+
end
|
|
662
|
+
local current = value.ClassObject
|
|
663
|
+
while current ~= nil do
|
|
664
|
+
if current == class then
|
|
665
|
+
return true
|
|
666
|
+
end
|
|
667
|
+
current = current.ParentClass
|
|
668
|
+
end
|
|
669
|
+
return false
|
|
670
|
+
end`).body.statements);
|
|
671
|
+
}
|
|
672
|
+
const accessors = this.helpers.get("accessors");
|
|
673
|
+
if (accessors) {
|
|
674
|
+
out.push(...parseLuau(`
|
|
675
|
+
local function ${accessors}(target, getters, setters)
|
|
676
|
+
return ${this.builtin("setmetatable").name}(target, {
|
|
677
|
+
-- Read by a spread, which copies what the getters give.
|
|
678
|
+
__tiluaGetters = getters,
|
|
679
|
+
__index = function(self, key)
|
|
680
|
+
local get = getters[key]
|
|
681
|
+
if get ~= nil then
|
|
682
|
+
return get(self)
|
|
683
|
+
end
|
|
684
|
+
return nil
|
|
685
|
+
end,
|
|
686
|
+
__newindex = function(self, key, value)
|
|
687
|
+
local set = setters[key]
|
|
688
|
+
if set ~= nil then
|
|
689
|
+
set(self, value)
|
|
690
|
+
return
|
|
691
|
+
end
|
|
692
|
+
if getters[key] ~= nil then
|
|
693
|
+
${this.builtin("error").name}("Cannot assign to '" .. ${this.builtin("tostring").name}(key) .. "': it has a getter and no setter", 2)
|
|
694
|
+
end
|
|
695
|
+
${this.builtin("rawset").name}(self, key, value)
|
|
696
|
+
end,
|
|
697
|
+
})
|
|
698
|
+
end`).body.statements);
|
|
699
|
+
}
|
|
531
700
|
const assign2 = this.helpers.get("assign");
|
|
532
701
|
if (assign2) {
|
|
533
|
-
out.push(
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
702
|
+
out.push(...parseLuau(`
|
|
703
|
+
local function ${assign2}(target, ...)
|
|
704
|
+
for i = 1, ${this.builtin("select").name}("#", ...) do
|
|
705
|
+
local source = (${this.builtin("select").name}(i, ...))
|
|
706
|
+
if source ~= nil then
|
|
707
|
+
for key, value in ${this.builtin("pairs").name}(source) do
|
|
708
|
+
target[key] = value
|
|
709
|
+
end
|
|
710
|
+
local meta = ${this.builtin("type").name}(source) == "table" and ${this.builtin("getmetatable").name}(source) or nil
|
|
711
|
+
local getters = ${this.builtin("type").name}(meta) == "table" and ${this.builtin("rawget").name}(meta, "__tiluaGetters") or nil
|
|
712
|
+
if getters ~= nil then
|
|
713
|
+
for key, get in ${this.builtin("pairs").name}(getters) do
|
|
714
|
+
target[key] = get(source)
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
return target
|
|
720
|
+
end`).body.statements);
|
|
544
721
|
}
|
|
545
722
|
out.push(...runtimes);
|
|
546
723
|
const concat = this.helpers.get("concat");
|
|
@@ -549,7 +726,7 @@ end`).body.statements);
|
|
|
549
726
|
local(["result"], [this.array(table([]))]),
|
|
550
727
|
numericFor("i", number(1), selectCount(this.builtin("select")), [
|
|
551
728
|
local(["part"], [call(this.builtin("select"), [identifier("i"), vararg()])]),
|
|
552
|
-
callStatement(call(
|
|
729
|
+
callStatement(call(this.move(), [
|
|
553
730
|
identifier("part"),
|
|
554
731
|
number(1),
|
|
555
732
|
unary("#", identifier("part")),
|
|
@@ -566,8 +743,8 @@ end`).body.statements);
|
|
|
566
743
|
runtimeDefinitions() {
|
|
567
744
|
const out = [];
|
|
568
745
|
for (const { index: index2, runtime: runtime2, name } of this.loweringUsed) {
|
|
569
|
-
const { plugin:
|
|
570
|
-
const source =
|
|
746
|
+
const { plugin: plugin3, from } = this.lowerings[index2];
|
|
747
|
+
const source = plugin3.runtime?.[runtime2];
|
|
571
748
|
if (source === void 0) {
|
|
572
749
|
this.diagnostics.push({
|
|
573
750
|
message: `'${from}' asked for a runtime it does not have: '${runtime2}'`,
|
|
@@ -662,6 +839,7 @@ end`).body.statements);
|
|
|
662
839
|
names.push("default");
|
|
663
840
|
break;
|
|
664
841
|
case "ExportNamedStatement": {
|
|
842
|
+
if (statement.typeOnly) break;
|
|
665
843
|
if (statement.source) {
|
|
666
844
|
const key = resolve3(statement.source);
|
|
667
845
|
if (key === void 0) break;
|
|
@@ -745,6 +923,26 @@ end`).body.statements);
|
|
|
745
923
|
for (let i = 0; i < locals.length; i += 100) declarations.push(local(locals.slice(i, i + 100), []));
|
|
746
924
|
return [...declarations, ...hoisted, ...requires, ...body];
|
|
747
925
|
}
|
|
926
|
+
/** `import("./x")`: the bundle's `import`, called with the module's name.
|
|
927
|
+
* It answers the module's exports table — what `import * as` names. */
|
|
928
|
+
importExpression(node) {
|
|
929
|
+
const context = this.options.module;
|
|
930
|
+
if (!context) {
|
|
931
|
+
this.report(node, "import() needs a bundle: build the project with @tilua/compiler");
|
|
932
|
+
return nil();
|
|
933
|
+
}
|
|
934
|
+
const source = node.source;
|
|
935
|
+
if (source.type !== "StringLiteral") {
|
|
936
|
+
this.report(source, "import() takes a string literal: which module it loads is decided when the project is built");
|
|
937
|
+
return nil();
|
|
938
|
+
}
|
|
939
|
+
const key = context.resolve(source.value, true);
|
|
940
|
+
if (key === void 0) {
|
|
941
|
+
this.report(source, `Cannot find module '${source.value}'`);
|
|
942
|
+
return nil();
|
|
943
|
+
}
|
|
944
|
+
return call(context.import ?? context.require, [string(key)]);
|
|
945
|
+
}
|
|
748
946
|
isRewritten(node) {
|
|
749
947
|
const binding = this.bindingByDeclaration.get(node);
|
|
750
948
|
return binding !== void 0 && this.rewrites.has(binding.id);
|
|
@@ -891,7 +1089,7 @@ end`).body.statements);
|
|
|
891
1089
|
case "VariableDeclaration":
|
|
892
1090
|
return this.variableDeclaration(node, "declare");
|
|
893
1091
|
case "FunctionDeclaration":
|
|
894
|
-
return [{ ...localFunction(this.name(node.name.name), this.functionBody(node.func)), attributes: node.attributes }];
|
|
1092
|
+
return [{ ...localFunction(this.name(node.name.name), this.functionBody(node.func)), attributes: this.lua51 ? void 0 : node.attributes }];
|
|
895
1093
|
case "FunctionDeclarationStatement":
|
|
896
1094
|
return [this.functionDeclarationStatement(node)];
|
|
897
1095
|
case "ClassDeclaration":
|
|
@@ -922,8 +1120,13 @@ end`).body.statements);
|
|
|
922
1120
|
// kept — Lua has no expression statement — and it cannot run
|
|
923
1121
|
// anything, since a call is a `CallStatement` instead, so dropping
|
|
924
1122
|
// it changes nothing but the noise.
|
|
925
|
-
case "ExpressionStatement":
|
|
926
|
-
|
|
1123
|
+
case "ExpressionStatement": {
|
|
1124
|
+
let expression = node.expression;
|
|
1125
|
+
while (expression.type === "ParenthesizedExpression") expression = expression.expression;
|
|
1126
|
+
if (expression.type !== "ImportExpression") return [];
|
|
1127
|
+
const call2 = this.importExpression(expression);
|
|
1128
|
+
return call2.type === "CallExpression" ? [this.from(callStatement(call2), node)] : [];
|
|
1129
|
+
}
|
|
927
1130
|
case "DoStatement":
|
|
928
1131
|
return [{ type: "DoStatement", body: this.block(node.body), ...spanOf(node) }];
|
|
929
1132
|
case "WhileStatement":
|
|
@@ -975,7 +1178,11 @@ end`).body.statements);
|
|
|
975
1178
|
let source = this.expression(node.iterator);
|
|
976
1179
|
if (form.viaIter) source = methodCall(source, "__iter", []);
|
|
977
1180
|
const variables = form.walks === "values" ? [this.names.fresh("_"), item] : [item];
|
|
978
|
-
|
|
1181
|
+
let iterators = form.walks === "iteration" ? [call(this.builtin("unpack"), [source, number(1), number(3)])] : [source];
|
|
1182
|
+
if (this.lua51 && form.walks === "values") {
|
|
1183
|
+
const kind = form.viaIter ? void 0 : this.options.types?.typeOf.get(node.iterator)?.kind;
|
|
1184
|
+
iterators = [call(kind === "array" || kind === "tuple" ? this.builtin("ipairs") : this.helper("each"), [source])];
|
|
1185
|
+
}
|
|
979
1186
|
return [genericFor(
|
|
980
1187
|
variables,
|
|
981
1188
|
iterators,
|
|
@@ -1055,11 +1262,12 @@ end`).body.statements);
|
|
|
1055
1262
|
const { build, accessors } = this.classHelpers;
|
|
1056
1263
|
const metamethods = `{ ${CLASS_METAMETHODS.map((name) => `"${name}"`).join(", ")} }`;
|
|
1057
1264
|
return parseLuau(`
|
|
1058
|
-
local function ${build}(base)
|
|
1265
|
+
local function ${build}(base, name)
|
|
1059
1266
|
local class = { __getters = {}, __setters = {} }
|
|
1060
1267
|
class.__index = class
|
|
1061
1268
|
class.ClassObject = class
|
|
1062
1269
|
class.ParentClass = base
|
|
1270
|
+
class.name = name
|
|
1063
1271
|
if base ~= nil then
|
|
1064
1272
|
setmetatable(class, { __index = base })
|
|
1065
1273
|
setmetatable(class.__getters, { __index = base.__getters })
|
|
@@ -1113,7 +1321,11 @@ end
|
|
|
1113
1321
|
* table is assigned to `self`, which a module has already declared. */
|
|
1114
1322
|
classStatements(node, self, declare) {
|
|
1115
1323
|
const { build, accessors } = this.classRuntime();
|
|
1116
|
-
const
|
|
1324
|
+
const called = node.name?.name ?? (node.type === "ClassExpression" ? this.classExpressionNames.get(node) : void 0) ?? "";
|
|
1325
|
+
const create = call(identifier(build), [
|
|
1326
|
+
node.superclass ? this.reference(node.superclass) : nil(),
|
|
1327
|
+
string(called)
|
|
1328
|
+
]);
|
|
1117
1329
|
const out = [declare !== void 0 ? local([declare], [create]) : assign([self], [create])];
|
|
1118
1330
|
const previous = this.classContext;
|
|
1119
1331
|
this.classContext = node;
|
|
@@ -1228,7 +1440,8 @@ end
|
|
|
1228
1440
|
},
|
|
1229
1441
|
isMethod,
|
|
1230
1442
|
func,
|
|
1231
|
-
|
|
1443
|
+
// `@native` and the like are Luau's.
|
|
1444
|
+
attributes: this.lua51 ? void 0 : node.attributes,
|
|
1232
1445
|
...spanOf(node)
|
|
1233
1446
|
};
|
|
1234
1447
|
}
|
|
@@ -1329,7 +1542,7 @@ end
|
|
|
1329
1542
|
if (element) bind(element.value, index(source, number(i + 1)), element.default);
|
|
1330
1543
|
});
|
|
1331
1544
|
if (pattern.rest) {
|
|
1332
|
-
const elements = call(
|
|
1545
|
+
const elements = call(this.move(), [
|
|
1333
1546
|
source,
|
|
1334
1547
|
number(pattern.elements.length + 1),
|
|
1335
1548
|
unary("#", source),
|
|
@@ -1440,8 +1653,15 @@ end
|
|
|
1440
1653
|
case "ArrayExpression":
|
|
1441
1654
|
return this.arrayExpression(node);
|
|
1442
1655
|
case "BinaryExpression": {
|
|
1656
|
+
if (node.operator === "??") return this.nullish(node);
|
|
1443
1657
|
const left = this.expression(node.left);
|
|
1444
1658
|
const right = this.expression(node.right);
|
|
1659
|
+
if (node.operator === "in") {
|
|
1660
|
+
return parenthesized(binary("~=", index(this.keptToOne(right), this.keptToOne(left)), nil()));
|
|
1661
|
+
}
|
|
1662
|
+
if (node.operator === "instanceof") {
|
|
1663
|
+
return call(this.helper("instanceof"), [this.keptToOne(left), this.keptToOne(right)]);
|
|
1664
|
+
}
|
|
1445
1665
|
if (this.lua51 && node.operator === "//") return this.floorDivide(left, right);
|
|
1446
1666
|
return binary(node.operator, left, right);
|
|
1447
1667
|
}
|
|
@@ -1468,6 +1688,8 @@ end
|
|
|
1468
1688
|
}
|
|
1469
1689
|
case "ParenthesizedExpression":
|
|
1470
1690
|
return parenthesized(this.expression(node.expression));
|
|
1691
|
+
case "ImportExpression":
|
|
1692
|
+
return this.importExpression(node);
|
|
1471
1693
|
// Types only: the value is the expression itself.
|
|
1472
1694
|
case "TypeAssertionExpression":
|
|
1473
1695
|
case "SatisfiesExpression":
|
|
@@ -1521,7 +1743,7 @@ end
|
|
|
1521
1743
|
const last2 = list.length - 1;
|
|
1522
1744
|
return lowered.length && !many.has(list[last2]) ? [...lowered.slice(0, -1), this.keptToOne(lowered[last2])] : lowered;
|
|
1523
1745
|
}
|
|
1524
|
-
const unpack = (value) => call(member(this.builtin("table"), "unpack"), [value]);
|
|
1746
|
+
const unpack = (value) => call(this.lua51 ? this.builtin("unpack") : member(this.builtin("table"), "unpack"), [value]);
|
|
1525
1747
|
const last = list[list.length - 1];
|
|
1526
1748
|
if (spreads.length === 1 && last.type === "SpreadElement") {
|
|
1527
1749
|
return [...list.slice(0, -1).map((a) => this.expression(a)), unpack(this.expression(last.argument))];
|
|
@@ -1535,11 +1757,21 @@ end
|
|
|
1535
1757
|
/** One link of an access chain, read from `object`. */
|
|
1536
1758
|
link(node, object2) {
|
|
1537
1759
|
switch (node.type) {
|
|
1538
|
-
case "MemberExpression":
|
|
1760
|
+
case "MemberExpression": {
|
|
1761
|
+
if (this.options.types?.lengths.has(node)) return unary("#", object2);
|
|
1762
|
+
const source = this.options.types?.methodReads.get(node);
|
|
1763
|
+
const lowered = source && this.loweredMethod(node.property.name, source, node.object, [], node.property, node);
|
|
1764
|
+
if (lowered) {
|
|
1765
|
+
const forwarded = call(calleePath(lowered.callee), [...lowered.prepend, vararg()]);
|
|
1766
|
+
return functionExpression(functionBody(lowered.passReceiver ? [] : ["_"], [returns([forwarded])], true));
|
|
1767
|
+
}
|
|
1539
1768
|
return member(object2, node.property.name);
|
|
1769
|
+
}
|
|
1540
1770
|
case "IndexExpression":
|
|
1541
1771
|
return index(object2, this.expression(node.index));
|
|
1542
1772
|
case "CallExpression": {
|
|
1773
|
+
const method = this.methodCalled(node);
|
|
1774
|
+
if (method) return method;
|
|
1543
1775
|
const lowered = this.loweredGlobalCall(node);
|
|
1544
1776
|
const args = this.arguments(node.arguments);
|
|
1545
1777
|
if (!lowered) return call(object2, args);
|
|
@@ -1562,6 +1794,24 @@ end
|
|
|
1562
1794
|
}
|
|
1563
1795
|
}
|
|
1564
1796
|
}
|
|
1797
|
+
/** `names.filter(names, f)`: a method of the value's metatable, read with
|
|
1798
|
+
* `.` and handed its `self` as the first argument. Written as a call of
|
|
1799
|
+
* the function the method really is — `array.filter(names, f)` — where
|
|
1800
|
+
* whoever declared the metatable says so; `undefined` leaves an ordinary
|
|
1801
|
+
* call, which is right for a string's Lua methods (`text.upper(text)`
|
|
1802
|
+
* reaches Lua's own `string.upper`). */
|
|
1803
|
+
methodCalled(node) {
|
|
1804
|
+
const callee = node.callee;
|
|
1805
|
+
if (callee.type !== "MemberExpression") return void 0;
|
|
1806
|
+
const source = this.options.types?.methodReads.get(callee);
|
|
1807
|
+
if (!source) return void 0;
|
|
1808
|
+
const spreads = node.arguments.map((a, i) => a.type === "SpreadElement" ? i : -1).filter((i) => i >= 0);
|
|
1809
|
+
if (!node.arguments.length || spreads.includes(0) || spreads.some((i) => i !== node.arguments.length - 1)) return void 0;
|
|
1810
|
+
const lowered = this.loweredMethod(callee.property.name, source, callee.object, node.arguments.slice(1), callee.property, node);
|
|
1811
|
+
if (!lowered) return void 0;
|
|
1812
|
+
const [self, ...rest] = this.arguments(node.arguments);
|
|
1813
|
+
return call(calleePath(lowered.callee), [...lowered.prepend, ...lowered.passReceiver ? [self] : [], ...rest]);
|
|
1814
|
+
}
|
|
1565
1815
|
// --------------------------------------------------------
|
|
1566
1816
|
// Optional chains
|
|
1567
1817
|
// --------------------------------------------------------
|
|
@@ -1607,7 +1857,7 @@ end
|
|
|
1607
1857
|
const segments = this.segments(chain);
|
|
1608
1858
|
const base = this.expression(chain.base);
|
|
1609
1859
|
const last = chain.links[chain.links.length - 1];
|
|
1610
|
-
if (base.type === "Identifier" && segments.length === 1 && last.type !== "CallExpression" && last.type !== "MethodCallExpression") {
|
|
1860
|
+
if (!this.lua51 && base.type === "Identifier" && segments.length === 1 && last.type !== "CallExpression" && last.type !== "MethodCallExpression") {
|
|
1611
1861
|
return {
|
|
1612
1862
|
type: "IfElseExpression",
|
|
1613
1863
|
clauses: [{ condition: binary("==", base, nil()), body: nil() }],
|
|
@@ -1628,6 +1878,32 @@ end
|
|
|
1628
1878
|
const func = functionExpression(functionBody([], statements, passesVarargs));
|
|
1629
1879
|
return call(parenthesized(func), passesVarargs ? [vararg()] : []);
|
|
1630
1880
|
}
|
|
1881
|
+
/** `a ?? b`: `a`, or `b` where `a` is nil — read only then. A `false`
|
|
1882
|
+
* stays, which is what sets it apart from `a or b`. A name is tested
|
|
1883
|
+
* in place; anything else is read once, into a local. */
|
|
1884
|
+
nullish(node) {
|
|
1885
|
+
const left = this.keptToOne(this.expression(node.left));
|
|
1886
|
+
const right = this.keptToOne(this.expression(node.right));
|
|
1887
|
+
if (!this.lua51 && left.type === "Identifier") {
|
|
1888
|
+
return {
|
|
1889
|
+
type: "IfElseExpression",
|
|
1890
|
+
clauses: [{ condition: binary("~=", left, nil()), body: left }],
|
|
1891
|
+
alternate: right,
|
|
1892
|
+
line: left.line,
|
|
1893
|
+
column: left.column
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
const ref = this.names.fresh("value");
|
|
1897
|
+
const at = identifier(ref);
|
|
1898
|
+
const statements = [
|
|
1899
|
+
local([ref], [left]),
|
|
1900
|
+
ifThen(binary("~=", at, nil()), [returns([at])]),
|
|
1901
|
+
returns([right])
|
|
1902
|
+
];
|
|
1903
|
+
const passesVarargs = statements.some(usesVararg);
|
|
1904
|
+
const func = functionExpression(functionBody([], statements, passesVarargs));
|
|
1905
|
+
return call(parenthesized(func), passesVarargs ? [vararg()] : []);
|
|
1906
|
+
}
|
|
1631
1907
|
/** `` `${a} any` `` -> `("%s any"):format(tostring(a))` */
|
|
1632
1908
|
interpolatedString(node) {
|
|
1633
1909
|
let format = "";
|
|
@@ -1709,12 +1985,12 @@ end
|
|
|
1709
1985
|
}));
|
|
1710
1986
|
const alternate = this.expression(node.alternate);
|
|
1711
1987
|
if (clauses.every((c) => alwaysTruthy(c.body))) {
|
|
1712
|
-
let
|
|
1988
|
+
let result2 = alternate;
|
|
1713
1989
|
for (let i = clauses.length - 1; i >= 0; i--) {
|
|
1714
1990
|
const { condition, body } = clauses[i];
|
|
1715
|
-
|
|
1991
|
+
result2 = binary("or", binary("and", condition, body), result2);
|
|
1716
1992
|
}
|
|
1717
|
-
return parenthesized(
|
|
1993
|
+
return parenthesized(result2);
|
|
1718
1994
|
}
|
|
1719
1995
|
const statement = {
|
|
1720
1996
|
type: "IfStatement",
|
|
@@ -1771,11 +2047,20 @@ end
|
|
|
1771
2047
|
current = [];
|
|
1772
2048
|
parts.push(this.expression(f.argument));
|
|
1773
2049
|
break;
|
|
2050
|
+
case "TableFieldAccessor":
|
|
2051
|
+
break;
|
|
1774
2052
|
}
|
|
1775
2053
|
}
|
|
1776
|
-
|
|
1777
|
-
if (
|
|
1778
|
-
|
|
2054
|
+
let made;
|
|
2055
|
+
if (parts.length === 0) made = table(current);
|
|
2056
|
+
else {
|
|
2057
|
+
if (current.length) parts.push(table(current));
|
|
2058
|
+
made = call(this.helper("assign"), [table([]), ...parts]);
|
|
2059
|
+
}
|
|
2060
|
+
const accessors = node.fields.filter((f) => f.type === "TableFieldAccessor");
|
|
2061
|
+
if (!accessors.length) return made;
|
|
2062
|
+
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)))));
|
|
2063
|
+
return call(this.helper("accessors"), [made, table2("get"), table2("set")]);
|
|
1779
2064
|
}
|
|
1780
2065
|
/** `[1, ...xs, 2]`. Without a spread it is a Luau sequence; with one, the
|
|
1781
2066
|
* runs of plain elements and the spread arrays are joined by `concat`. */
|
|
@@ -2039,15 +2324,15 @@ async function loadLowerings(modules) {
|
|
|
2039
2324
|
for (const module of modules) {
|
|
2040
2325
|
try {
|
|
2041
2326
|
const loaded = await import(pathToFileURL(module.file).href);
|
|
2042
|
-
const
|
|
2043
|
-
if (!
|
|
2327
|
+
const plugin3 = loaded.default;
|
|
2328
|
+
if (!plugin3 || typeof plugin3 !== "object") {
|
|
2044
2329
|
problems.push({
|
|
2045
2330
|
file: module.file,
|
|
2046
2331
|
message: `'${module.from}' lowering module has no default export`
|
|
2047
2332
|
});
|
|
2048
2333
|
continue;
|
|
2049
2334
|
}
|
|
2050
|
-
lowerings.push({ plugin:
|
|
2335
|
+
lowerings.push({ plugin: plugin3, from: module.from, library: module.library });
|
|
2051
2336
|
} catch (error) {
|
|
2052
2337
|
problems.push({
|
|
2053
2338
|
file: module.file,
|
|
@@ -2059,8 +2344,8 @@ async function loadLowerings(modules) {
|
|
|
2059
2344
|
}
|
|
2060
2345
|
|
|
2061
2346
|
// src/bundle.ts
|
|
2062
|
-
import { readFileSync as
|
|
2063
|
-
import { dirname, relative, resolve as resolve2 } from "path";
|
|
2347
|
+
import { readFileSync as readFileSync3, statSync } from "fs";
|
|
2348
|
+
import { basename, dirname, extname, posix, relative, resolve as resolve2 } from "path";
|
|
2064
2349
|
import {
|
|
2065
2350
|
parse,
|
|
2066
2351
|
analyzeScopes,
|
|
@@ -2068,17 +2353,18 @@ import {
|
|
|
2068
2353
|
moduleExports,
|
|
2069
2354
|
resolveModulePath,
|
|
2070
2355
|
resolveTypeLibraries,
|
|
2356
|
+
importExpressions,
|
|
2071
2357
|
directivesOf,
|
|
2072
2358
|
applyDirectives,
|
|
2073
2359
|
UNUSED_EXPECT_ERROR,
|
|
2074
2360
|
ParseError,
|
|
2075
2361
|
LexError,
|
|
2076
|
-
luauString as
|
|
2362
|
+
luauString as luauString4
|
|
2077
2363
|
} from "@tilua/parser";
|
|
2078
2364
|
import { parse as parseLuau2, parseExpressionFromSource as parseExpressionFromSource2, print } from "luau-parser";
|
|
2079
2365
|
|
|
2080
2366
|
// src/linemap.ts
|
|
2081
|
-
import { luauString as
|
|
2367
|
+
import { luauString as luauString3 } from "@tilua/parser";
|
|
2082
2368
|
function statementsOf(node) {
|
|
2083
2369
|
const out = [];
|
|
2084
2370
|
const visit = (value) => {
|
|
@@ -2111,7 +2397,7 @@ function buildLineMap(emitted, printed, origins) {
|
|
|
2111
2397
|
}
|
|
2112
2398
|
function lineMapSource(map) {
|
|
2113
2399
|
const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
|
|
2114
|
-
const quoted = (s) =>
|
|
2400
|
+
const quoted = (s) => luauString3(s.replace(/\\/g, "/"));
|
|
2115
2401
|
return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
|
|
2116
2402
|
}
|
|
2117
2403
|
|
|
@@ -2131,16 +2417,17 @@ async function bundle(options) {
|
|
|
2131
2417
|
const file = queue.shift();
|
|
2132
2418
|
if (sources.has(file)) continue;
|
|
2133
2419
|
const parsed = parseFile(file, diagnostics, directives);
|
|
2134
|
-
if (!parsed) return { modules: [], diagnostics };
|
|
2420
|
+
if (!parsed) return { files: [], modules: [], diagnostics };
|
|
2135
2421
|
const { program: program3, text } = parsed;
|
|
2136
|
-
const scopes = analyzeScopes(program3);
|
|
2422
|
+
const scopes = analyzeScopes(program3, { noLib: config?.noLib });
|
|
2137
2423
|
for (const d of scopes.diagnostics) {
|
|
2138
2424
|
diagnostics.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "scope" });
|
|
2139
2425
|
}
|
|
2140
2426
|
sources.set(file, { file, name: nameOf(file), text, program: program3, scopes });
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2427
|
+
const dynamic = importExpressions(program3).flatMap((node) => node.source.type === "StringLiteral" ? [node.source.value] : []);
|
|
2428
|
+
for (const specifier of [...importedSpecifiers(program3), ...dynamic]) {
|
|
2429
|
+
const target2 = resolveModulePath(file, specifier, config);
|
|
2430
|
+
if (target2 && !target2.endsWith(".d.tilua")) queue.push(target2);
|
|
2144
2431
|
}
|
|
2145
2432
|
}
|
|
2146
2433
|
const analysis = analyzeModules([...sources.values()], config);
|
|
@@ -2152,6 +2439,9 @@ async function bundle(options) {
|
|
|
2152
2439
|
const names = Names.from(...[...sources.values()].map((s) => s.program));
|
|
2153
2440
|
const G = names.fresh("G");
|
|
2154
2441
|
const requireExpression = member(identifier(G), "require");
|
|
2442
|
+
const importExpression = member(identifier(G), "import");
|
|
2443
|
+
const mode = options.output ?? config?.output ?? "single";
|
|
2444
|
+
const target = options.target ?? config?.target ?? "luau";
|
|
2155
2445
|
const modules = /* @__PURE__ */ new Map();
|
|
2156
2446
|
const origins = /* @__PURE__ */ new WeakMap();
|
|
2157
2447
|
const pending = [entry];
|
|
@@ -2160,38 +2450,48 @@ async function bundle(options) {
|
|
|
2160
2450
|
if (modules.has(file)) continue;
|
|
2161
2451
|
const source = sources.get(file);
|
|
2162
2452
|
const shown = relative(root, file).replace(/\\/g, "/");
|
|
2163
|
-
const
|
|
2453
|
+
const imports = /* @__PURE__ */ new Set();
|
|
2454
|
+
const dynamicImports = /* @__PURE__ */ new Set();
|
|
2455
|
+
const lowered2 = lower(source.program, source.scopes, {
|
|
2164
2456
|
names,
|
|
2165
2457
|
types: analysis.types.get(file),
|
|
2166
2458
|
lowerings,
|
|
2167
|
-
target
|
|
2459
|
+
target,
|
|
2460
|
+
noLib: config?.noLib,
|
|
2168
2461
|
source: source.text,
|
|
2169
2462
|
file: shown,
|
|
2170
2463
|
module: {
|
|
2171
2464
|
name: source.name,
|
|
2172
2465
|
require: requireExpression,
|
|
2173
|
-
|
|
2466
|
+
import: importExpression,
|
|
2467
|
+
// One file has one line map. Several have one each, and a
|
|
2468
|
+
// module reads the one of the file it is in.
|
|
2469
|
+
lines: mode === "single" ? `${G}.lines` : `${G}.maps[${luauString4(source.name)}]`,
|
|
2174
2470
|
fail: `${G}.fail`,
|
|
2175
2471
|
arrays: `${G}.arrays`,
|
|
2176
|
-
resolve: (specifier) => {
|
|
2177
|
-
const
|
|
2178
|
-
if (!
|
|
2179
|
-
pending.push(
|
|
2180
|
-
|
|
2472
|
+
resolve: (specifier, dynamic) => {
|
|
2473
|
+
const found = resolveModulePath(file, specifier, config);
|
|
2474
|
+
if (!found || found.endsWith(".d.tilua") || !sources.has(found)) return void 0;
|
|
2475
|
+
pending.push(found);
|
|
2476
|
+
const name = sources.get(found).name;
|
|
2477
|
+
(dynamic ? dynamicImports : imports).add(name);
|
|
2478
|
+
return name;
|
|
2181
2479
|
}
|
|
2182
2480
|
}
|
|
2183
2481
|
});
|
|
2184
|
-
for (const d of
|
|
2185
|
-
for (const statement of allStatements(
|
|
2186
|
-
const line =
|
|
2482
|
+
for (const d of lowered2.diagnostics) diagnostics.push({ file, ...d, category: "module" });
|
|
2483
|
+
for (const statement of allStatements(lowered2.statements)) {
|
|
2484
|
+
const line = lowered2.origins.get(statement);
|
|
2187
2485
|
if (line !== void 0) origins.set(statement, { file: shown, line });
|
|
2188
2486
|
}
|
|
2189
2487
|
modules.set(file, {
|
|
2190
2488
|
name: source.name,
|
|
2191
|
-
statements:
|
|
2192
|
-
exportsName:
|
|
2193
|
-
info:
|
|
2194
|
-
usesScriptArgs:
|
|
2489
|
+
statements: lowered2.statements,
|
|
2490
|
+
exportsName: lowered2.exportsName,
|
|
2491
|
+
info: lowered2.module,
|
|
2492
|
+
usesScriptArgs: lowered2.usesScriptArgs,
|
|
2493
|
+
imports,
|
|
2494
|
+
dynamicImports
|
|
2195
2495
|
});
|
|
2196
2496
|
}
|
|
2197
2497
|
for (const [file, fileDirectives] of directives) {
|
|
@@ -2210,54 +2510,168 @@ async function bundle(options) {
|
|
|
2210
2510
|
const reported = diagnostics.filter((d) => d.category !== "type" || !moduleProblems.has(`${d.file}:${d.line}:${d.column}:${d.message}`));
|
|
2211
2511
|
diagnostics.length = 0;
|
|
2212
2512
|
diagnostics.push(...reported);
|
|
2213
|
-
const
|
|
2513
|
+
const lowered = [...modules.values()];
|
|
2514
|
+
const moduleNames = lowered.map((m) => m.name);
|
|
2214
2515
|
if (diagnostics.some((d) => d.category === "syntax" || d.category === "scope" || d.category === "module")) {
|
|
2215
|
-
return { modules: moduleNames, diagnostics };
|
|
2216
|
-
}
|
|
2217
|
-
const
|
|
2516
|
+
return { files: [], modules: moduleNames, diagnostics };
|
|
2517
|
+
}
|
|
2518
|
+
const several = mode !== "single";
|
|
2519
|
+
const layout = layoutOf(mode, modules.get(entry), lowered, options.outFile);
|
|
2520
|
+
const usesScriptArgs = lowered.some((m) => m.usesScriptArgs);
|
|
2521
|
+
const elsewhere = /* @__PURE__ */ new Map();
|
|
2522
|
+
for (const file of layout.slice(1)) {
|
|
2523
|
+
for (const module of file.modules) elsewhere.set(module.name, loadPath(layout[0].path, file.path, target));
|
|
2524
|
+
}
|
|
2525
|
+
const program2 = parseLuau2(runtime(G, {
|
|
2526
|
+
several,
|
|
2527
|
+
target,
|
|
2528
|
+
files: elsewhere,
|
|
2529
|
+
imports: lowered.some((m) => m.dynamicImports.size > 0)
|
|
2530
|
+
}));
|
|
2218
2531
|
const assignment = program2.body.statements[1];
|
|
2219
|
-
if (
|
|
2532
|
+
if (usesScriptArgs) {
|
|
2220
2533
|
program2.body.statements.unshift(local(["scriptArgs"], [table([{ type: "TableFieldPositional", value: { type: "VarargExpression" } }])]));
|
|
2221
2534
|
program2.body.statements.push(...parseLuau2(`${G}.arrays[scriptArgs] = true`).body.statements);
|
|
2535
|
+
if (several) program2.body.statements.push(...parseLuau2(`${G}.scriptArgs = scriptArgs`).body.statements);
|
|
2222
2536
|
}
|
|
2223
2537
|
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;
|
|
2224
2538
|
if (!modulesTable) throw new Error("@tilua/compiler: the bundle runtime has no modules table");
|
|
2225
|
-
for (const module of modules
|
|
2226
|
-
|
|
2227
|
-
const fields = [];
|
|
2228
|
-
if (exported.length) fields.push(field("names", table(exported.map((n) => field(n, boolean(true))))));
|
|
2229
|
-
if (links.length) {
|
|
2230
|
-
fields.push(field("links", table(links.map((link) => field(link.name, table([
|
|
2231
|
-
{ type: "TableFieldPositional", value: string(link.module) },
|
|
2232
|
-
...link.imported === void 0 ? [] : [{ type: "TableFieldPositional", value: string(link.imported) }]
|
|
2233
|
-
]))))));
|
|
2234
|
-
}
|
|
2235
|
-
if (stars.length) fields.push(field("stars", table(stars.map((star) => ({ type: "TableFieldPositional", value: string(star) })))));
|
|
2236
|
-
fields.push(field("load", functionExpression(functionBody([module.exportsName], module.statements, true))));
|
|
2237
|
-
modulesTable.fields.push({ type: "TableFieldComputed", key: string(module.name), value: table(fields) });
|
|
2539
|
+
for (const module of layout[0].modules) {
|
|
2540
|
+
modulesTable.fields.push({ type: "TableFieldComputed", key: string(module.name), value: moduleTable(module) });
|
|
2238
2541
|
}
|
|
2239
2542
|
const ok = names.fresh("ok");
|
|
2240
|
-
const
|
|
2543
|
+
const result2 = names.fresh("result");
|
|
2241
2544
|
const entryStatements = parseLuau2(`
|
|
2242
|
-
local ${ok}, ${
|
|
2243
|
-
return ${G}.require(${
|
|
2545
|
+
local ${ok}, ${result2} = xpcall(function()
|
|
2546
|
+
return ${G}.require(${luauString4(sources.get(entry).name)})
|
|
2244
2547
|
end, ${G}.fail)
|
|
2245
2548
|
if not ${ok} then
|
|
2246
|
-
error(${
|
|
2549
|
+
error(${result2}, 0)
|
|
2247
2550
|
end
|
|
2248
|
-
${modules.get(entry).info.exports ? `return ${
|
|
2551
|
+
${modules.get(entry).info.exports ? `return ${result2}` : ""}
|
|
2249
2552
|
`).body.statements;
|
|
2250
2553
|
program2.body.statements.push(...entryStatements);
|
|
2251
2554
|
const map = buildLineMap(program2, parseLuau2(print(program2)), origins);
|
|
2252
|
-
if (map.size) {
|
|
2253
|
-
|
|
2555
|
+
if (map.size || several) {
|
|
2556
|
+
const mapStatements = [assign(
|
|
2254
2557
|
[member(identifier(G), "lines")],
|
|
2255
2558
|
[parseExpressionFromSource2(lineMapSource(map))]
|
|
2256
|
-
)
|
|
2559
|
+
)];
|
|
2560
|
+
if (several) mapStatements.push(...mapRegistration(G, layout[0], `${G}.lines`));
|
|
2561
|
+
program2.body.statements.splice(program2.body.statements.length - entryStatements.length, 0, ...mapStatements);
|
|
2562
|
+
}
|
|
2563
|
+
const files = [{ path: layout[0].path, code: print(program2) + "\n", modules: layout[0].modules.map((m) => m.name) }];
|
|
2564
|
+
for (const file of layout.slice(1)) {
|
|
2565
|
+
const body = [];
|
|
2566
|
+
if (file.modules.some((m) => m.usesScriptArgs)) body.push(...parseLuau2(`local scriptArgs = ${G}.scriptArgs`).body.statements);
|
|
2567
|
+
for (const module of file.modules) {
|
|
2568
|
+
body.push(assign(
|
|
2569
|
+
[index(member(identifier(G), "modules"), string(module.name))],
|
|
2570
|
+
[moduleTable(module)]
|
|
2571
|
+
));
|
|
2572
|
+
}
|
|
2573
|
+
const chunk = program([returns([functionExpression(functionBody([G], body))])]);
|
|
2574
|
+
const lines = buildLineMap(chunk, parseLuau2(print(chunk)), origins);
|
|
2575
|
+
body.push(...mapRegistration(G, file, lineMapSource(lines)));
|
|
2576
|
+
files.push({ path: file.path, code: print(chunk) + "\n", modules: file.modules.map((m) => m.name) });
|
|
2577
|
+
}
|
|
2578
|
+
return { code: files[0].code, files, modules: moduleNames, diagnostics };
|
|
2579
|
+
}
|
|
2580
|
+
function moduleTable(module) {
|
|
2581
|
+
const { names: exported, links, stars } = module.info;
|
|
2582
|
+
const fields = [];
|
|
2583
|
+
if (exported.length) fields.push(field("names", table(exported.map((n) => field(n, boolean(true))))));
|
|
2584
|
+
if (links.length) {
|
|
2585
|
+
fields.push(field("links", table(links.map((link) => field(link.name, table([
|
|
2586
|
+
{ type: "TableFieldPositional", value: string(link.module) },
|
|
2587
|
+
...link.imported === void 0 ? [] : [{ type: "TableFieldPositional", value: string(link.imported) }]
|
|
2588
|
+
]))))));
|
|
2589
|
+
}
|
|
2590
|
+
if (stars.length) fields.push(field("stars", table(stars.map((star) => ({ type: "TableFieldPositional", value: string(star) })))));
|
|
2591
|
+
fields.push(field("load", functionExpression(functionBody([module.exportsName], module.statements, true))));
|
|
2592
|
+
return table(fields);
|
|
2593
|
+
}
|
|
2594
|
+
function layoutOf(mode, entry, modules, outFile) {
|
|
2595
|
+
const extension = outFile !== void 0 && extname(outFile) || ".luau";
|
|
2596
|
+
const entryStem = outFile !== void 0 ? basename(outFile, extname(outFile)) : posix.basename(entry.name);
|
|
2597
|
+
const others = modules.filter((m) => m !== entry);
|
|
2598
|
+
if (mode === "single") return [{ path: `${entryStem}${extension}`, modules: [entry, ...others] }];
|
|
2599
|
+
if (mode === "modules") {
|
|
2600
|
+
const pathOf = (m) => `${m.name.split("/").map((part) => part === ".." ? "__" : part).join("/")}${extension}`;
|
|
2601
|
+
return [entry, ...others].map((m) => ({ path: pathOf(m), modules: [m] }));
|
|
2602
|
+
}
|
|
2603
|
+
const byName = new Map(modules.map((m) => [m.name, m]));
|
|
2604
|
+
const reach = (root) => {
|
|
2605
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2606
|
+
const stack = [root];
|
|
2607
|
+
while (stack.length) {
|
|
2608
|
+
const name = stack.pop();
|
|
2609
|
+
if (seen.has(name)) continue;
|
|
2610
|
+
seen.add(name);
|
|
2611
|
+
stack.push(...byName.get(name)?.imports ?? []);
|
|
2612
|
+
}
|
|
2613
|
+
return seen;
|
|
2614
|
+
};
|
|
2615
|
+
const inEntry = reach(entry.name);
|
|
2616
|
+
const roots = [...new Set(modules.flatMap((m) => [...m.dynamicImports]))].filter((root) => !inEntry.has(root));
|
|
2617
|
+
const reached = new Map(roots.map((root) => [root, reach(root)]));
|
|
2618
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2619
|
+
for (const module of others) {
|
|
2620
|
+
if (inEntry.has(module.name)) continue;
|
|
2621
|
+
const by = roots.filter((root) => reached.get(root).has(module.name));
|
|
2622
|
+
const key = by.join("\n");
|
|
2623
|
+
if (!groups.has(key)) groups.set(key, { roots: by, modules: [] });
|
|
2624
|
+
groups.get(key).modules.push(module);
|
|
2625
|
+
}
|
|
2626
|
+
const files = [{ path: `${entryStem}${extension}`, modules: modules.filter((m) => inEntry.has(m.name)) }];
|
|
2627
|
+
const taken = /* @__PURE__ */ new Set([entryStem]);
|
|
2628
|
+
for (const group of groups.values()) {
|
|
2629
|
+
const label = group.roots.length === 1 ? group.roots[0].replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "chunk" : "shared";
|
|
2630
|
+
let stem = `${entryStem}_${label}`;
|
|
2631
|
+
for (let n = 2; taken.has(stem); n++) stem = `${entryStem}_${label}_${n}`;
|
|
2632
|
+
taken.add(stem);
|
|
2633
|
+
files.push({ path: `${stem}${extension}`, modules: group.modules });
|
|
2634
|
+
}
|
|
2635
|
+
return files;
|
|
2636
|
+
}
|
|
2637
|
+
function loadPath(entryPath, file, target) {
|
|
2638
|
+
const path = posix.relative(posix.dirname(entryPath), file);
|
|
2639
|
+
if (target === "lua51") return path;
|
|
2640
|
+
const bare = path.slice(0, path.length - posix.extname(path).length);
|
|
2641
|
+
return bare.startsWith("../") ? bare : `./${bare}`;
|
|
2642
|
+
}
|
|
2643
|
+
function mapRegistration(G, file, map) {
|
|
2644
|
+
const extension = posix.extname(file.path);
|
|
2645
|
+
const parts = file.path.slice(0, file.path.length - extension.length).split(/[/.]/).filter((part) => part !== "");
|
|
2646
|
+
const entry = `${G}.sources[#${G}.sources + 1]`;
|
|
2647
|
+
return parseLuau2([
|
|
2648
|
+
`${entry} = { { ${parts.map(luauString4).join(", ")} }, ${map}, ${luauString4(extension.slice(1))} }`,
|
|
2649
|
+
...file.modules.map((module) => `${G}.maps[${luauString4(module.name)}] = ${G}.sources[#${G}.sources][2]`)
|
|
2650
|
+
].join("\n")).body.statements;
|
|
2651
|
+
}
|
|
2652
|
+
function loader(G, target) {
|
|
2653
|
+
if (target === "lua51") {
|
|
2654
|
+
return `
|
|
2655
|
+
-- From beside this file, wherever that is: its own path says where.
|
|
2656
|
+
local folder = ""
|
|
2657
|
+
if debug ~= nil and debug.getinfo ~= nil then
|
|
2658
|
+
local at = debug.getinfo(${G}.load, "S").source
|
|
2659
|
+
if at:sub(1, 1) == "@" then
|
|
2660
|
+
folder = at:match("^@(.*[/\\\\])") or ""
|
|
2661
|
+
end
|
|
2662
|
+
end
|
|
2663
|
+
local chunk, problem = loadfile(folder .. file)
|
|
2664
|
+
if chunk == nil then
|
|
2665
|
+
error(problem, 0)
|
|
2666
|
+
end
|
|
2667
|
+
chunk()(${G})`;
|
|
2257
2668
|
}
|
|
2258
|
-
return
|
|
2669
|
+
return `
|
|
2670
|
+
require(file)(${G})`;
|
|
2259
2671
|
}
|
|
2260
|
-
function runtime(G) {
|
|
2672
|
+
function runtime(G, options) {
|
|
2673
|
+
const { several } = options;
|
|
2674
|
+
const files = [...options.files].map(([name, file]) => `[${luauString4(name)}] = ${luauString4(file)}`).join(", ");
|
|
2261
2675
|
return `
|
|
2262
2676
|
local ${G}
|
|
2263
2677
|
${G} = {
|
|
@@ -2305,7 +2719,7 @@ ${G} = {
|
|
|
2305
2719
|
return message
|
|
2306
2720
|
end
|
|
2307
2721
|
local lines = ${G}.lines
|
|
2308
|
-
if lines == nil then
|
|
2722
|
+
if lines == nil ${several ? `and #${G}.sources == 0 ` : ""}then
|
|
2309
2723
|
return message .. (trace or "")
|
|
2310
2724
|
end
|
|
2311
2725
|
-- The bundle's name, as a position in a message spells it: where this
|
|
@@ -2325,6 +2739,81 @@ ${G} = {
|
|
|
2325
2739
|
chunk = info.short_src
|
|
2326
2740
|
end
|
|
2327
2741
|
end
|
|
2742
|
+
${several ? `
|
|
2743
|
+
-- A position in this file is read by this file's map. One in another
|
|
2744
|
+
-- of the bundle's files is read by that file's: the one whose name
|
|
2745
|
+
-- the position ends with, by the most folders where several do. A
|
|
2746
|
+
-- message says nothing about which file it starts in, so nothing is
|
|
2747
|
+
-- guessed from it here.
|
|
2748
|
+
local prefix = nil
|
|
2749
|
+
if chunk ~= nil then
|
|
2750
|
+
prefix = chunk:gsub("%p", "%%%0")
|
|
2751
|
+
end
|
|
2752
|
+
local function place(line)
|
|
2753
|
+
local origin = lines ~= nil and lines[tonumber(line)] or nil
|
|
2754
|
+
if origin == nil then
|
|
2755
|
+
return nil
|
|
2756
|
+
end
|
|
2757
|
+
return origin[1] .. ":" .. tostring(origin[2])
|
|
2758
|
+
end
|
|
2759
|
+
local function elsewhere(at, line)
|
|
2760
|
+
-- Already a place in the project.
|
|
2761
|
+
if at:sub(-6) == ".tilua" then
|
|
2762
|
+
return nil
|
|
2763
|
+
end
|
|
2764
|
+
local parts = {}
|
|
2765
|
+
for part in at:gmatch("[^/\\\\%.]+") do
|
|
2766
|
+
parts[#parts + 1] = part
|
|
2767
|
+
end
|
|
2768
|
+
local map, most, tied = nil, 0, false
|
|
2769
|
+
for _, source in ipairs(${G}.sources) do
|
|
2770
|
+
local names = source[1]
|
|
2771
|
+
local last = #parts
|
|
2772
|
+
if parts[last] == source[3] then
|
|
2773
|
+
last = last - 1
|
|
2774
|
+
end
|
|
2775
|
+
local same = 0
|
|
2776
|
+
while same < #names and same < last and names[#names - same] == parts[last - same] do
|
|
2777
|
+
same = same + 1
|
|
2778
|
+
end
|
|
2779
|
+
if same > most then
|
|
2780
|
+
map, most, tied = source[2], same, false
|
|
2781
|
+
elseif same == most then
|
|
2782
|
+
tied = true
|
|
2783
|
+
end
|
|
2784
|
+
end
|
|
2785
|
+
-- Two files it could as well be: a wrong place is worse than none.
|
|
2786
|
+
local origin = map ~= nil and not tied and map[tonumber(line)] or nil
|
|
2787
|
+
if origin == nil then
|
|
2788
|
+
return nil
|
|
2789
|
+
end
|
|
2790
|
+
return origin[1] .. ":" .. tostring(origin[2])
|
|
2791
|
+
end
|
|
2792
|
+
local mapped = message
|
|
2793
|
+
if prefix ~= nil then
|
|
2794
|
+
mapped = mapped:gsub(prefix .. ":(%d+)", function(line)
|
|
2795
|
+
return place(line)
|
|
2796
|
+
end)
|
|
2797
|
+
end
|
|
2798
|
+
mapped = mapped:gsub("(%S+):(%d+)", elsewhere)
|
|
2799
|
+
local frames = {}
|
|
2800
|
+
for frame in (trace or ""):gmatch("[^\\n]+") do
|
|
2801
|
+
local at = nil
|
|
2802
|
+
if prefix ~= nil then
|
|
2803
|
+
local line = frame:match(prefix .. ":(%d+)")
|
|
2804
|
+
if line ~= nil then
|
|
2805
|
+
at = place(line)
|
|
2806
|
+
end
|
|
2807
|
+
end
|
|
2808
|
+
if at == nil then
|
|
2809
|
+
for source, line in frame:gmatch("(%S+):(%d+)") do
|
|
2810
|
+
at = elsewhere(source, line)
|
|
2811
|
+
if at ~= nil then
|
|
2812
|
+
break
|
|
2813
|
+
end
|
|
2814
|
+
end
|
|
2815
|
+
end
|
|
2816
|
+
if at ~= nil then` : `
|
|
2328
2817
|
if chunk == nil then
|
|
2329
2818
|
chunk = message:match("^(.-):%d+:")
|
|
2330
2819
|
end
|
|
@@ -2346,8 +2835,9 @@ ${G} = {
|
|
|
2346
2835
|
for frame in (trace or ""):gmatch("[^\\n]+") do
|
|
2347
2836
|
local line = frame:match(prefix .. ":(%d+)")
|
|
2348
2837
|
local at = line ~= nil and place(line) or nil
|
|
2349
|
-
if at ~= nil then
|
|
2350
|
-
|
|
2838
|
+
if at ~= nil then`}
|
|
2839
|
+
-- Luau writes \`function name\`; Lua 5.1, \`function 'name'\`.
|
|
2840
|
+
local name = frame:match("function ([%w_.:]+)") or frame:match("function '([^']+)'")
|
|
2351
2841
|
frames[#frames + 1] = " at " .. at .. (name ~= nil and (" (" .. name .. ")") or "")
|
|
2352
2842
|
end
|
|
2353
2843
|
end
|
|
@@ -2356,13 +2846,36 @@ ${G} = {
|
|
|
2356
2846
|
end
|
|
2357
2847
|
return mapped .. "\\n" .. table.concat(frames, "\\n")
|
|
2358
2848
|
end,
|
|
2359
|
-
records = {}
|
|
2849
|
+
records = {},${several ? `
|
|
2850
|
+
-- The file each module outside this one is in, as \`load\` is handed it.
|
|
2851
|
+
files = { ${files} },
|
|
2852
|
+
loaded = {},
|
|
2853
|
+
-- Every file's line map: by the module, and by the file's name.
|
|
2854
|
+
maps = {},
|
|
2855
|
+
sources = {},
|
|
2856
|
+
-- Another of the bundle's files: a function that adds its modules here.
|
|
2857
|
+
load = function(file)
|
|
2858
|
+
if ${G}.loaded[file] then
|
|
2859
|
+
return
|
|
2860
|
+
end
|
|
2861
|
+
${G}.loaded[file] = true${loader(G, options.target)}
|
|
2862
|
+
end,` : ""}${options.imports ? `
|
|
2863
|
+
-- \`import("./x")\`: the module's exports, asked for where the code runs
|
|
2864
|
+
-- rather than before its module does. \`require\` does the rest \u2014 and
|
|
2865
|
+
-- loads the file the module is in, if it is not this one.
|
|
2866
|
+
import = function(name)
|
|
2867
|
+
return ${G}.require(name)
|
|
2868
|
+
end,` : ""}
|
|
2360
2869
|
require = function(name)
|
|
2361
2870
|
local record = ${G}.records[name]
|
|
2362
2871
|
if record ~= nil then
|
|
2363
2872
|
return record.exports
|
|
2364
2873
|
end
|
|
2365
|
-
local module = ${G}.modules[name]
|
|
2874
|
+
local module = ${G}.modules[name]${several ? `
|
|
2875
|
+
if module == nil then
|
|
2876
|
+
${G}.load(${G}.files[name])
|
|
2877
|
+
module = ${G}.modules[name]
|
|
2878
|
+
end` : ""}
|
|
2366
2879
|
local initialized = {}
|
|
2367
2880
|
local exports = setmetatable({}, {
|
|
2368
2881
|
__index = function(_, key)
|
|
@@ -2381,7 +2894,7 @@ ${G} = {
|
|
|
2381
2894
|
return source[link[2]]
|
|
2382
2895
|
end
|
|
2383
2896
|
if module.stars ~= nil and key ~= "default" then
|
|
2384
|
-
for _, star in module.stars do
|
|
2897
|
+
for _, star in ipairs(module.stars) do
|
|
2385
2898
|
local value = ${G}.require(star)[key]
|
|
2386
2899
|
if value ~= nil then
|
|
2387
2900
|
return value
|
|
@@ -2422,7 +2935,7 @@ function parseFile(file, diagnostics, directives) {
|
|
|
2422
2935
|
}
|
|
2423
2936
|
let text;
|
|
2424
2937
|
try {
|
|
2425
|
-
text =
|
|
2938
|
+
text = readFileSync3(file, "utf8");
|
|
2426
2939
|
} catch {
|
|
2427
2940
|
diagnostics.push({ file, message: "Cannot read file", line: 1, column: 1, category: "module" });
|
|
2428
2941
|
return void 0;
|
|
@@ -2456,7 +2969,7 @@ function parsedLibrary(file) {
|
|
|
2456
2969
|
}
|
|
2457
2970
|
const cached = libraryCache.get(file);
|
|
2458
2971
|
if (cached && cached.stamp === stamp) return cached.program;
|
|
2459
|
-
const program2 = parse(
|
|
2972
|
+
const program2 = parse(readFileSync3(file, "utf8"));
|
|
2460
2973
|
libraryCache.set(file, { stamp, program: program2 });
|
|
2461
2974
|
return program2;
|
|
2462
2975
|
}
|
|
@@ -2467,6 +2980,8 @@ function analyzeModules(modules, config) {
|
|
|
2467
2980
|
const libraries = config ? resolveTypeLibraries(config) : { files: [], lowerings: [], problems: [] };
|
|
2468
2981
|
for (const p of libraries.problems) out.push({ file: p.file, message: p.message, line: p.line ?? 1, column: p.column ?? 1, category: "config" });
|
|
2469
2982
|
const libs = libraries.files.map(parsedLibrary);
|
|
2983
|
+
const noLib = config?.noLib ?? false;
|
|
2984
|
+
const checked = libs.length > 0 || !noLib;
|
|
2470
2985
|
const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
|
|
2471
2986
|
const byFile = new Map(modules.map((m) => [m.file, m]));
|
|
2472
2987
|
const exportsCache = /* @__PURE__ */ new Map();
|
|
@@ -2479,12 +2994,12 @@ function analyzeModules(modules, config) {
|
|
|
2479
2994
|
const done = analyzed.get(file);
|
|
2480
2995
|
if (done) return done;
|
|
2481
2996
|
const remembered = moduleAnalyses.get(file);
|
|
2482
|
-
if (remembered && remembered.setting === settingFor && libs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === libs[i]) && remembered.program === program2 && [...remembered.read].every(([
|
|
2997
|
+
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)) {
|
|
2483
2998
|
analyzed.set(file, remembered.result);
|
|
2484
2999
|
readWhile.set(file, remembered.read);
|
|
2485
3000
|
return remembered.result;
|
|
2486
3001
|
}
|
|
2487
|
-
const scopes = analyzeScopes(program2, { builtinGlobals: globals, reportUndeclared:
|
|
3002
|
+
const scopes = analyzeScopes(program2, { builtinGlobals: globals, reportUndeclared: checked, noLib });
|
|
2488
3003
|
const said = [];
|
|
2489
3004
|
for (const d of scopes.diagnostics) {
|
|
2490
3005
|
if (d.kind !== "undeclared") continue;
|
|
@@ -2494,34 +3009,35 @@ function analyzeModules(modules, config) {
|
|
|
2494
3009
|
const types = analyzeTypes(program2, scopes, {
|
|
2495
3010
|
libs,
|
|
2496
3011
|
resolveModule: resolverFor(file),
|
|
2497
|
-
|
|
3012
|
+
noLib,
|
|
3013
|
+
reportUnknownTypes: checked
|
|
2498
3014
|
});
|
|
2499
3015
|
for (const d of types.diagnostics) {
|
|
2500
3016
|
const at = d.node;
|
|
2501
3017
|
said.push({ file, message: d.message, line: at.line.start, column: at.column.start, category: "type" });
|
|
2502
3018
|
}
|
|
2503
|
-
const
|
|
3019
|
+
const result2 = { types, diagnostics: said };
|
|
2504
3020
|
if (partials === before) {
|
|
2505
|
-
analyzed.set(file,
|
|
2506
|
-
const
|
|
3021
|
+
analyzed.set(file, result2);
|
|
3022
|
+
const read3 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
2507
3023
|
const own = stampOf(file);
|
|
2508
|
-
if (own !== void 0)
|
|
2509
|
-
readWhile.set(file,
|
|
2510
|
-
if ([...
|
|
2511
|
-
moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program: program2, read:
|
|
3024
|
+
if (own !== void 0) read3.set(file, own);
|
|
3025
|
+
readWhile.set(file, read3);
|
|
3026
|
+
if ([...read3.values()].every((stamp) => stamp !== void 0)) {
|
|
3027
|
+
moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program: program2, read: read3, result: result2 });
|
|
2512
3028
|
}
|
|
2513
3029
|
}
|
|
2514
|
-
return
|
|
3030
|
+
return result2;
|
|
2515
3031
|
};
|
|
2516
3032
|
const resolverFor = (file) => (specifier) => {
|
|
2517
3033
|
const target = resolveModulePath(file, specifier, config);
|
|
2518
3034
|
if (!target) return void 0;
|
|
2519
3035
|
const noteRead = () => {
|
|
2520
|
-
const
|
|
2521
|
-
readWhile.set(file,
|
|
3036
|
+
const read3 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
|
|
3037
|
+
readWhile.set(file, read3);
|
|
2522
3038
|
const stamp = stampOf(target);
|
|
2523
|
-
if (stamp !== void 0)
|
|
2524
|
-
for (const [below, belowStamp] of readWhile.get(target) ?? [])
|
|
3039
|
+
if (stamp !== void 0) read3.set(target, stamp);
|
|
3040
|
+
for (const [below, belowStamp] of readWhile.get(target) ?? []) read3.set(below, belowStamp);
|
|
2525
3041
|
};
|
|
2526
3042
|
if (inProgress.has(target)) {
|
|
2527
3043
|
partials++;
|
|
@@ -2535,15 +3051,15 @@ function analyzeModules(modules, config) {
|
|
|
2535
3051
|
let program2 = byFile.get(target)?.program;
|
|
2536
3052
|
if (!program2) {
|
|
2537
3053
|
try {
|
|
2538
|
-
program2 = parse(
|
|
3054
|
+
program2 = parse(readFileSync3(target, "utf8"));
|
|
2539
3055
|
} catch {
|
|
2540
3056
|
return void 0;
|
|
2541
3057
|
}
|
|
2542
3058
|
}
|
|
2543
3059
|
inProgress.add(target);
|
|
2544
3060
|
try {
|
|
2545
|
-
const scopes = analyzeScopes(program2, { builtinGlobals: globals });
|
|
2546
|
-
const types = byFile.has(target) ? analyzeModule(target, program2).types : analyzeTypes(program2, scopes, { libs, resolveModule: resolverFor(target), diagnostics: false });
|
|
3061
|
+
const scopes = analyzeScopes(program2, { builtinGlobals: globals, noLib });
|
|
3062
|
+
const types = byFile.has(target) ? analyzeModule(target, program2).types : analyzeTypes(program2, scopes, { libs, noLib, resolveModule: resolverFor(target), diagnostics: false });
|
|
2547
3063
|
const exports = moduleExports(program2, scopes, types, resolverFor(target));
|
|
2548
3064
|
exportsCache.set(target, exports);
|
|
2549
3065
|
noteRead();
|