@tilua/compiler 3.1.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/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 import_node_fs2 = require("fs");
36
+ var import_node_fs3 = require("fs");
37
37
  var import_node_path2 = require("path");
38
- var import_parser6 = require("@tilua/parser");
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 import_parser4 = require("@tilua/parser");
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();
@@ -250,11 +321,11 @@ var Lowerer = class {
250
321
  origins = /* @__PURE__ */ new WeakMap();
251
322
  run() {
252
323
  const body = this.options.module ? this.module(this.options.module) : this.topLevel(this.source.body.statements);
324
+ const scriptArgs = this.usesScriptArgs && !this.options.module ? [local(["scriptArgs"], [this.array(table([{ type: "TableFieldPositional", value: vararg() }]))])] : [];
253
325
  const helpers = this.helperDefinitions();
254
326
  const captured = [...this.builtins].map(([global, local2]) => local([local2], [identifier(global)]));
255
- const scriptArgs = this.usesScriptArgs && !this.options.module ? [local(["scriptArgs"], [table([{ type: "TableFieldPositional", value: vararg() }])])] : [];
256
327
  return {
257
- statements: [...scriptArgs, ...captured, ...helpers, ...this.classRuntimeStatements(), ...body],
328
+ statements: [...captured, ...helpers, ...scriptArgs, ...this.classRuntimeStatements(), ...body],
258
329
  exportsName: this.exportsName,
259
330
  module: this.info,
260
331
  diagnostics: this.diagnostics,
@@ -301,14 +372,43 @@ 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) {
307
- name = this.names.fresh(`tilua_${kind}`);
382
+ name = this.names.fresh(`tilua_${kind === "array" ? "asArray" : kind}`);
308
383
  this.helpers.set(kind, name);
309
384
  }
310
385
  return identifier(name);
311
386
  }
387
+ // `typeof v` tells an array from an object, which Luau cannot: both are
388
+ // tables. So every array the program makes — `[a, b]`, a rest parameter,
389
+ // what `:map` answers — is written down as one, in a table that holds its
390
+ // keys weakly, and `typeof` looks there. A table nothing wrote down (one a
391
+ // Lua library made) is told by its type where that says, and by its shape
392
+ // where it does not.
393
+ /** `value`, a new array, written down as one. */
394
+ array(value) {
395
+ const marked = call(this.helper("array"), [value]);
396
+ this.marked.add(marked);
397
+ return marked;
398
+ }
399
+ /** The calls `array` made: each answers the one table it was given. */
400
+ marked = /* @__PURE__ */ new WeakSet();
401
+ /** Luau reaching the record of arrays: the bundle's, or this file's own. */
402
+ arrays() {
403
+ return this.options.module?.arrays ?? this.helper("arrays").name;
404
+ }
405
+ typeofExpression(node) {
406
+ const known = this.options.types?.typeofTables.get(node);
407
+ let argument = node.argument;
408
+ while (argument.type === "ParenthesizedExpression") argument = argument.expression;
409
+ const value = this.expression(argument);
410
+ return call(this.helper("typeof"), known ? [value, string(known)] : [this.keptToOne(value)]);
411
+ }
312
412
  /** The local a library's runtime table gets in this file, emitting it the
313
413
  * first time something asks. Keyed by library and by the key the library
314
414
  * itself chose, so two libraries' tables never collide. */
@@ -376,7 +476,7 @@ var Lowerer = class {
376
476
  const type = this.options.types?.typeOf.get(argument);
377
477
  return {
378
478
  type,
379
- typeText: type ? (0, import_parser4.formatType)(type) : void 0,
479
+ typeText: type ? (0, import_parser5.formatType)(type) : void 0,
380
480
  source: this.writtenSource(argument),
381
481
  declaration: declaration ? this.writtenSource(declaration) : void 0,
382
482
  spread: argument.type === "SpreadElement"
@@ -398,7 +498,11 @@ var Lowerer = class {
398
498
  /** The language's own lowering, then each library's, in load order. A
399
499
  * runtime is named by its lowering's place here (`loweringRuntime`). */
400
500
  get lowerings() {
401
- return this.allLowerings ??= [languageLowering, ...this.options.lowerings ?? []];
501
+ return this.allLowerings ??= [
502
+ languageLowering,
503
+ ...this.options.noLib ? [] : [standardLowering],
504
+ ...this.options.lowerings ?? []
505
+ ];
402
506
  }
403
507
  allLowerings;
404
508
  /** What `receiver:method(...)` becomes, when Luau alone would not run it.
@@ -412,22 +516,28 @@ var Lowerer = class {
412
516
  * (`console:log`), the last library loaded first. No answer leaves an
413
517
  * ordinary Luau method call: `text:upper()` reaches Lua's own. */
414
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) {
415
526
  const lowerings = this.lowerings;
416
- const source = this.options.types?.methodSources.get(node);
417
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();
418
528
  if (!asked.some((i) => lowerings[i].plugin.methodCall)) return void 0;
419
- const receiver = this.options.types?.typeOf.get(node.object);
420
- const receiverGlobal = this.globalName(node.object);
421
- const info = this.lazyArguments(node.arguments);
529
+ const receiver = this.options.types?.typeOf.get(object2);
530
+ const receiverGlobal = this.globalName(object2);
531
+ const info = this.lazyArguments(args);
422
532
  for (const i of asked) {
423
- const plugin2 = lowerings[i].plugin;
424
- if (!plugin2.methodCall) continue;
425
- const answer = plugin2.methodCall({
426
- method: node.method.name,
533
+ const plugin3 = lowerings[i].plugin;
534
+ if (!plugin3.methodCall) continue;
535
+ const answer = plugin3.methodCall({
536
+ method,
427
537
  receiver,
428
538
  receiverGlobal,
429
- argumentCount: node.arguments.length,
430
- at: this.callSite(node.method),
539
+ argumentCount: args.length,
540
+ at: this.callSite(name),
431
541
  get arguments() {
432
542
  return info();
433
543
  },
@@ -451,9 +561,9 @@ var Lowerer = class {
451
561
  if (name === void 0) return void 0;
452
562
  const info = this.lazyArguments(node.arguments);
453
563
  for (let i = lowerings.length - 1; i >= 0; i--) {
454
- const plugin2 = lowerings[i].plugin;
455
- if (!plugin2.globalCall) continue;
456
- const answer = plugin2.globalCall({
564
+ const plugin3 = lowerings[i].plugin;
565
+ if (!plugin3.globalCall) continue;
566
+ const answer = plugin3.globalCall({
457
567
  name,
458
568
  at: this.callSite(node.callee),
459
569
  get arguments() {
@@ -503,6 +613,114 @@ var Lowerer = class {
503
613
  }
504
614
  helperDefinitions() {
505
615
  const out = [];
616
+ const runtimes = this.runtimeDefinitions();
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
+ }
643
+ const marker = this.helpers.get("array");
644
+ const typeOf = this.helpers.get("typeof");
645
+ if (marker || typeOf) {
646
+ const arrays = this.arrays();
647
+ const own = this.helpers.get("arrays");
648
+ if (own) {
649
+ out.push(...(0, import_luau_parser.parse)(`local ${own} = ${this.builtin("setmetatable").name}({}, { __mode = "k" })`).body.statements);
650
+ }
651
+ if (marker) {
652
+ out.push(...(0, import_luau_parser.parse)(`
653
+ local function ${marker}(t)
654
+ ${arrays}[t] = true
655
+ return t
656
+ end`).body.statements);
657
+ }
658
+ if (typeOf) {
659
+ out.push(...(0, import_luau_parser.parse)(`
660
+ local function ${typeOf}(value, known)
661
+ local name = ${this.builtin(this.lua51 ? "type" : "typeof").name}(value)
662
+ if name ~= "table" then
663
+ return name
664
+ end
665
+ if known ~= nil then
666
+ return known
667
+ end
668
+ if ${arrays}[value] then
669
+ return "array"
670
+ end
671
+ if ${this.builtin("getmetatable").name}(value) ~= nil then
672
+ return "object"
673
+ end
674
+ if #value > 0 then
675
+ return "array"
676
+ end
677
+ return "object"
678
+ end`).body.statements);
679
+ }
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
+ }
506
724
  const assign2 = this.helpers.get("assign");
507
725
  if (assign2) {
508
726
  out.push(localFunction(assign2, functionBody(["target"], [
@@ -517,28 +735,14 @@ var Lowerer = class {
517
735
  returns([identifier("target")])
518
736
  ], true)));
519
737
  }
520
- for (const { index: index2, runtime: runtime2, name } of this.loweringUsed) {
521
- const { plugin: plugin2, from } = this.lowerings[index2];
522
- const source = plugin2.runtime?.[runtime2];
523
- if (source === void 0) {
524
- this.diagnostics.push({
525
- message: `'${from}' asked for a runtime it does not have: '${runtime2}'`,
526
- line: 1,
527
- column: 1
528
- });
529
- continue;
530
- }
531
- const lines = this.options.module?.lines ?? "nil";
532
- const fail = this.options.module?.fail ?? "nil";
533
- out.push(...(0, import_luau_parser.parse)(source.replace(/__NAME__/g, name).replace(/__LINES__/g, lines).replace(/__FAIL__/g, fail)).body.statements);
534
- }
738
+ out.push(...runtimes);
535
739
  const concat = this.helpers.get("concat");
536
740
  if (concat) {
537
741
  out.push(localFunction(concat, functionBody([], [
538
- local(["result"], [table([])]),
742
+ local(["result"], [this.array(table([]))]),
539
743
  numericFor("i", number(1), selectCount(this.builtin("select")), [
540
744
  local(["part"], [call(this.builtin("select"), [identifier("i"), vararg()])]),
541
- callStatement(call(member(this.builtin("table"), "move"), [
745
+ callStatement(call(this.move(), [
542
746
  identifier("part"),
543
747
  number(1),
544
748
  unary("#", identifier("part")),
@@ -551,6 +755,26 @@ var Lowerer = class {
551
755
  }
552
756
  return out;
553
757
  }
758
+ /** The runtime tables the file's libraries were asked for. */
759
+ runtimeDefinitions() {
760
+ const out = [];
761
+ for (const { index: index2, runtime: runtime2, name } of this.loweringUsed) {
762
+ const { plugin: plugin3, from } = this.lowerings[index2];
763
+ const source = plugin3.runtime?.[runtime2];
764
+ if (source === void 0) {
765
+ this.diagnostics.push({
766
+ message: `'${from}' asked for a runtime it does not have: '${runtime2}'`,
767
+ line: 1,
768
+ column: 1
769
+ });
770
+ continue;
771
+ }
772
+ const lines = this.options.module?.lines ?? "nil";
773
+ const fail = this.options.module?.fail ?? "nil";
774
+ out.push(...(0, import_luau_parser.parse)(source.replace(/__NAME__/g, name).replace(/__LINES__/g, lines).replace(/__FAIL__/g, fail).replace(/__ARRAY__/g, () => this.helper("array").name)).body.statements);
775
+ }
776
+ return out;
777
+ }
554
778
  // --------------------------------------------------------
555
779
  // Modules
556
780
  // --------------------------------------------------------
@@ -631,6 +855,7 @@ var Lowerer = class {
631
855
  names.push("default");
632
856
  break;
633
857
  case "ExportNamedStatement": {
858
+ if (statement.typeOnly) break;
634
859
  if (statement.source) {
635
860
  const key = resolve4(statement.source);
636
861
  if (key === void 0) break;
@@ -714,6 +939,26 @@ var Lowerer = class {
714
939
  for (let i = 0; i < locals.length; i += 100) declarations.push(local(locals.slice(i, i + 100), []));
715
940
  return [...declarations, ...hoisted, ...requires, ...body];
716
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
+ }
717
962
  isRewritten(node) {
718
963
  const binding = this.bindingByDeclaration.get(node);
719
964
  return binding !== void 0 && this.rewrites.has(binding.id);
@@ -860,7 +1105,7 @@ var Lowerer = class {
860
1105
  case "VariableDeclaration":
861
1106
  return this.variableDeclaration(node, "declare");
862
1107
  case "FunctionDeclaration":
863
- 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 }];
864
1109
  case "FunctionDeclarationStatement":
865
1110
  return [this.functionDeclarationStatement(node)];
866
1111
  case "ClassDeclaration":
@@ -891,8 +1136,13 @@ var Lowerer = class {
891
1136
  // kept — Lua has no expression statement — and it cannot run
892
1137
  // anything, since a call is a `CallStatement` instead, so dropping
893
1138
  // it changes nothing but the noise.
894
- case "ExpressionStatement":
895
- return [];
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
+ }
896
1146
  case "DoStatement":
897
1147
  return [{ type: "DoStatement", body: this.block(node.body), ...spanOf(node) }];
898
1148
  case "WhileStatement":
@@ -944,7 +1194,11 @@ var Lowerer = class {
944
1194
  let source = this.expression(node.iterator);
945
1195
  if (form.viaIter) source = methodCall(source, "__iter", []);
946
1196
  const variables = form.walks === "values" ? [this.names.fresh("_"), item] : [item];
947
- const iterators = form.walks === "iteration" ? [call(this.builtin("unpack"), [source, number(1), number(3)])] : [source];
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
+ }
948
1202
  return [genericFor(
949
1203
  variables,
950
1204
  iterators,
@@ -1197,7 +1451,8 @@ end
1197
1451
  },
1198
1452
  isMethod,
1199
1453
  func,
1200
- attributes: node.attributes,
1454
+ // `@native` and the like are Luau's.
1455
+ attributes: this.lua51 ? void 0 : node.attributes,
1201
1456
  ...spanOf(node)
1202
1457
  };
1203
1458
  }
@@ -1298,12 +1553,12 @@ end
1298
1553
  if (element) bind(element.value, index(source, number(i + 1)), element.default);
1299
1554
  });
1300
1555
  if (pattern.rest) {
1301
- const elements = call(member(this.builtin("table"), "move"), [
1556
+ const elements = call(this.move(), [
1302
1557
  source,
1303
1558
  number(pattern.elements.length + 1),
1304
1559
  unary("#", source),
1305
1560
  number(1),
1306
- table([])
1561
+ this.array(table([]))
1307
1562
  ]);
1308
1563
  const rest = this.restTarget(pattern.rest, elements, mode, after);
1309
1564
  after.push(...rest.then);
@@ -1357,7 +1612,7 @@ end
1357
1612
  const names = [];
1358
1613
  for (const p of params) {
1359
1614
  if (p.rest) {
1360
- prelude.push(local([this.name(p.name)], [table([{ type: "TableFieldPositional", value: vararg() }])]));
1615
+ prelude.push(local([this.name(p.name)], [this.array(table([{ type: "TableFieldPositional", value: vararg() }]))]));
1361
1616
  continue;
1362
1617
  }
1363
1618
  const name = p.pattern ? this.names.fresh("arg") : this.name(p.name);
@@ -1409,12 +1664,20 @@ end
1409
1664
  case "ArrayExpression":
1410
1665
  return this.arrayExpression(node);
1411
1666
  case "BinaryExpression": {
1667
+ if (node.operator === "??") return this.nullish(node);
1412
1668
  const left = this.expression(node.left);
1413
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
+ }
1414
1676
  if (this.lua51 && node.operator === "//") return this.floorDivide(left, right);
1415
1677
  return binary(node.operator, left, right);
1416
1678
  }
1417
1679
  case "UnaryExpression":
1680
+ if (node.operator === "typeof") return this.typeofExpression(node);
1418
1681
  return unary(node.operator, this.expression(node.argument));
1419
1682
  case "SuperExpression":
1420
1683
  return this.superClassReference(node);
@@ -1436,6 +1699,8 @@ end
1436
1699
  }
1437
1700
  case "ParenthesizedExpression":
1438
1701
  return parenthesized(this.expression(node.expression));
1702
+ case "ImportExpression":
1703
+ return this.importExpression(node);
1439
1704
  // Types only: the value is the expression itself.
1440
1705
  case "TypeAssertionExpression":
1441
1706
  case "SatisfiesExpression":
@@ -1469,7 +1734,7 @@ end
1469
1734
  * kept to one there, `f((g()))`. Every value is taken only where the
1470
1735
  * program asks for them, as an array's last element: `[g()]`. */
1471
1736
  keptToOne(value) {
1472
- return expandsToMany(value) ? parenthesized(value) : value;
1737
+ return expandsToMany(value) && !this.marked.has(value) ? parenthesized(value) : value;
1473
1738
  }
1474
1739
  /** Is this `scriptArgs` the language's own, not a name the file declared? */
1475
1740
  isLanguageGlobal(node) {
@@ -1489,7 +1754,7 @@ end
1489
1754
  const last2 = list.length - 1;
1490
1755
  return lowered.length && !many.has(list[last2]) ? [...lowered.slice(0, -1), this.keptToOne(lowered[last2])] : lowered;
1491
1756
  }
1492
- 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]);
1493
1758
  const last = list[list.length - 1];
1494
1759
  if (spreads.length === 1 && last.type === "SpreadElement") {
1495
1760
  return [...list.slice(0, -1).map((a) => this.expression(a)), unpack(this.expression(last.argument))];
@@ -1503,11 +1768,21 @@ end
1503
1768
  /** One link of an access chain, read from `object`. */
1504
1769
  link(node, object2) {
1505
1770
  switch (node.type) {
1506
- 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
+ }
1507
1779
  return member(object2, node.property.name);
1780
+ }
1508
1781
  case "IndexExpression":
1509
1782
  return index(object2, this.expression(node.index));
1510
1783
  case "CallExpression": {
1784
+ const method = this.methodCalled(node);
1785
+ if (method) return method;
1511
1786
  const lowered = this.loweredGlobalCall(node);
1512
1787
  const args = this.arguments(node.arguments);
1513
1788
  if (!lowered) return call(object2, args);
@@ -1530,6 +1805,24 @@ end
1530
1805
  }
1531
1806
  }
1532
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
+ }
1533
1826
  // --------------------------------------------------------
1534
1827
  // Optional chains
1535
1828
  // --------------------------------------------------------
@@ -1575,7 +1868,7 @@ end
1575
1868
  const segments = this.segments(chain);
1576
1869
  const base = this.expression(chain.base);
1577
1870
  const last = chain.links[chain.links.length - 1];
1578
- 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") {
1579
1872
  return {
1580
1873
  type: "IfElseExpression",
1581
1874
  clauses: [{ condition: binary("==", base, nil()), body: nil() }],
@@ -1596,6 +1889,32 @@ end
1596
1889
  const func = functionExpression(functionBody([], statements, passesVarargs));
1597
1890
  return call(parenthesized(func), passesVarargs ? [vararg()] : []);
1598
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
+ }
1599
1918
  /** `` `${a} any` `` -> `("%s any"):format(tostring(a))` */
1600
1919
  interpolatedString(node) {
1601
1920
  let format = "";
@@ -1677,12 +1996,12 @@ end
1677
1996
  }));
1678
1997
  const alternate = this.expression(node.alternate);
1679
1998
  if (clauses.every((c) => alwaysTruthy(c.body))) {
1680
- let result = alternate;
1999
+ let result2 = alternate;
1681
2000
  for (let i = clauses.length - 1; i >= 0; i--) {
1682
2001
  const { condition, body } = clauses[i];
1683
- result = binary("or", binary("and", condition, body), result);
2002
+ result2 = binary("or", binary("and", condition, body), result2);
1684
2003
  }
1685
- return parenthesized(result);
2004
+ return parenthesized(result2);
1686
2005
  }
1687
2006
  const statement = {
1688
2007
  type: "IfStatement",
@@ -1739,18 +2058,27 @@ end
1739
2058
  current = [];
1740
2059
  parts.push(this.expression(f.argument));
1741
2060
  break;
2061
+ case "TableFieldAccessor":
2062
+ break;
1742
2063
  }
1743
2064
  }
1744
- if (parts.length === 0) return table(current);
1745
- if (current.length) parts.push(table(current));
1746
- return call(this.helper("assign"), [table([]), ...parts]);
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")]);
1747
2075
  }
1748
2076
  /** `[1, ...xs, 2]`. Without a spread it is a Luau sequence; with one, the
1749
2077
  * runs of plain elements and the spread arrays are joined by `concat`. */
1750
2078
  arrayExpression(node) {
1751
2079
  const elements = packs(node.elements).items;
1752
2080
  if (!elements.some((e) => e.type === "SpreadElement")) {
1753
- return table(elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) })));
2081
+ return this.array(table(elements.map((e) => ({ type: "TableFieldPositional", value: this.expression(e) }))));
1754
2082
  }
1755
2083
  const parts = [];
1756
2084
  let current = [];
@@ -2000,7 +2328,7 @@ var CLASS_METAMETHODS = [
2000
2328
  ];
2001
2329
 
2002
2330
  // src/linemap.ts
2003
- var import_parser5 = require("@tilua/parser");
2331
+ var import_parser6 = require("@tilua/parser");
2004
2332
  function statementsOf(node) {
2005
2333
  const out = [];
2006
2334
  const visit = (value) => {
@@ -2033,7 +2361,7 @@ function buildLineMap(emitted, printed, origins) {
2033
2361
  }
2034
2362
  function lineMapSource(map) {
2035
2363
  const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
2036
- const quoted = (s) => (0, import_parser5.luauString)(s.replace(/\\/g, "/"));
2364
+ const quoted = (s) => (0, import_parser6.luauString)(s.replace(/\\/g, "/"));
2037
2365
  return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
2038
2366
  }
2039
2367
 
@@ -2045,15 +2373,15 @@ async function loadLowerings(modules) {
2045
2373
  for (const module2 of modules) {
2046
2374
  try {
2047
2375
  const loaded = await import((0, import_node_url.pathToFileURL)(module2.file).href);
2048
- const plugin2 = loaded.default;
2049
- if (!plugin2 || typeof plugin2 !== "object") {
2376
+ const plugin3 = loaded.default;
2377
+ if (!plugin3 || typeof plugin3 !== "object") {
2050
2378
  problems.push({
2051
2379
  file: module2.file,
2052
2380
  message: `'${module2.from}' lowering module has no default export`
2053
2381
  });
2054
2382
  continue;
2055
2383
  }
2056
- lowerings.push({ plugin: plugin2, from: module2.from, library: module2.library });
2384
+ lowerings.push({ plugin: plugin3, from: module2.from, library: module2.library });
2057
2385
  } catch (error) {
2058
2386
  problems.push({
2059
2387
  file: module2.file,
@@ -2080,16 +2408,17 @@ async function bundle(options) {
2080
2408
  const file = queue.shift();
2081
2409
  if (sources.has(file)) continue;
2082
2410
  const parsed = parseFile(file, diagnostics, directives);
2083
- if (!parsed) return { modules: [], diagnostics };
2411
+ if (!parsed) return { files: [], modules: [], diagnostics };
2084
2412
  const { program: program3, text } = parsed;
2085
- const scopes = (0, import_parser6.analyzeScopes)(program3);
2413
+ const scopes = (0, import_parser7.analyzeScopes)(program3, { noLib: config?.noLib });
2086
2414
  for (const d of scopes.diagnostics) {
2087
2415
  diagnostics.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "scope" });
2088
2416
  }
2089
2417
  sources.set(file, { file, name: nameOf(file), text, program: program3, scopes });
2090
- for (const specifier of importedSpecifiers(program3)) {
2091
- const target = (0, import_parser6.resolveModulePath)(file, specifier, config);
2092
- if (target && !target.endsWith(".d.tilua")) queue.push(target);
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);
2093
2422
  }
2094
2423
  }
2095
2424
  const analysis = analyzeModules([...sources.values()], config);
@@ -2101,6 +2430,9 @@ async function bundle(options) {
2101
2430
  const names = Names.from(...[...sources.values()].map((s) => s.program));
2102
2431
  const G = names.fresh("G");
2103
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";
2104
2436
  const modules = /* @__PURE__ */ new Map();
2105
2437
  const origins = /* @__PURE__ */ new WeakMap();
2106
2438
  const pending = [entry];
@@ -2109,106 +2441,235 @@ async function bundle(options) {
2109
2441
  if (modules.has(file)) continue;
2110
2442
  const source = sources.get(file);
2111
2443
  const shown = (0, import_node_path2.relative)(root, file).replace(/\\/g, "/");
2112
- const lowered = lower(source.program, source.scopes, {
2444
+ const imports = /* @__PURE__ */ new Set();
2445
+ const dynamicImports = /* @__PURE__ */ new Set();
2446
+ const lowered2 = lower(source.program, source.scopes, {
2113
2447
  names,
2114
2448
  types: analysis.types.get(file),
2115
2449
  lowerings,
2116
- target: options.target ?? config?.target,
2450
+ target,
2451
+ noLib: config?.noLib,
2117
2452
  source: source.text,
2118
2453
  file: shown,
2119
2454
  module: {
2120
2455
  name: source.name,
2121
2456
  require: requireExpression,
2122
- lines: `${G}.lines`,
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)}]`,
2123
2461
  fail: `${G}.fail`,
2124
- resolve: (specifier) => {
2125
- const target = (0, import_parser6.resolveModulePath)(file, specifier, config);
2126
- if (!target || target.endsWith(".d.tilua") || !sources.has(target)) return void 0;
2127
- pending.push(target);
2128
- return sources.get(target).name;
2462
+ arrays: `${G}.arrays`,
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;
2129
2470
  }
2130
2471
  }
2131
2472
  });
2132
- for (const d of lowered.diagnostics) diagnostics.push({ file, ...d, category: "module" });
2133
- for (const statement of allStatements(lowered.statements)) {
2134
- const line = lowered.origins.get(statement);
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);
2135
2476
  if (line !== void 0) origins.set(statement, { file: shown, line });
2136
2477
  }
2137
2478
  modules.set(file, {
2138
2479
  name: source.name,
2139
- statements: lowered.statements,
2140
- exportsName: lowered.exportsName,
2141
- info: lowered.module,
2142
- usesScriptArgs: lowered.usesScriptArgs
2480
+ statements: lowered2.statements,
2481
+ exportsName: lowered2.exportsName,
2482
+ info: lowered2.module,
2483
+ usesScriptArgs: lowered2.usesScriptArgs,
2484
+ imports,
2485
+ dynamicImports
2143
2486
  });
2144
2487
  }
2145
2488
  for (const [file, fileDirectives] of directives) {
2146
2489
  const semantic = diagnostics.filter((d) => d.file === file && (d.category === "scope" || d.category === "type"));
2147
- const { kept, unusedExpectErrors } = (0, import_parser6.applyDirectives)(fileDirectives, semantic, (d) => d.line);
2490
+ const { kept, unusedExpectErrors } = (0, import_parser7.applyDirectives)(fileDirectives, semantic, (d) => d.line);
2148
2491
  const suppressed = new Set(semantic.filter((d) => !kept.includes(d)));
2149
2492
  const remaining = diagnostics.filter((d) => !suppressed.has(d));
2150
2493
  diagnostics.length = 0;
2151
2494
  diagnostics.push(...remaining);
2152
2495
  if (options.typeCheck === false) continue;
2153
2496
  for (const d of unusedExpectErrors) {
2154
- diagnostics.push({ file, message: import_parser6.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
2497
+ diagnostics.push({ file, message: import_parser7.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
2155
2498
  }
2156
2499
  }
2157
2500
  const moduleProblems = new Set(diagnostics.filter((d) => d.category === "module").map((d) => `${d.file}:${d.line}:${d.column}:${d.message}`));
2158
2501
  const reported = diagnostics.filter((d) => d.category !== "type" || !moduleProblems.has(`${d.file}:${d.line}:${d.column}:${d.message}`));
2159
2502
  diagnostics.length = 0;
2160
2503
  diagnostics.push(...reported);
2161
- const moduleNames = [...modules.values()].map((m) => m.name);
2504
+ const lowered = [...modules.values()];
2505
+ const moduleNames = lowered.map((m) => m.name);
2162
2506
  if (diagnostics.some((d) => d.category === "syntax" || d.category === "scope" || d.category === "module")) {
2163
- return { modules: moduleNames, diagnostics };
2164
- }
2165
- const program2 = (0, import_luau_parser2.parse)(runtime(G));
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
+ }));
2166
2522
  const assignment = program2.body.statements[1];
2167
- if ([...modules.values()].some((m) => m.usesScriptArgs)) {
2523
+ if (usesScriptArgs) {
2168
2524
  program2.body.statements.unshift(local(["scriptArgs"], [table([{ type: "TableFieldPositional", value: { type: "VarargExpression" } }])]));
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);
2169
2527
  }
2170
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;
2171
2529
  if (!modulesTable) throw new Error("@tilua/compiler: the bundle runtime has no modules table");
2172
- for (const module2 of modules.values()) {
2173
- const { names: exported, links, stars } = module2.info;
2174
- const fields = [];
2175
- if (exported.length) fields.push(field("names", table(exported.map((n) => field(n, boolean(true))))));
2176
- if (links.length) {
2177
- fields.push(field("links", table(links.map((link) => field(link.name, table([
2178
- { type: "TableFieldPositional", value: string(link.module) },
2179
- ...link.imported === void 0 ? [] : [{ type: "TableFieldPositional", value: string(link.imported) }]
2180
- ]))))));
2181
- }
2182
- if (stars.length) fields.push(field("stars", table(stars.map((star) => ({ type: "TableFieldPositional", value: string(star) })))));
2183
- fields.push(field("load", functionExpression(functionBody([module2.exportsName], module2.statements, true))));
2184
- 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) });
2185
2532
  }
2186
2533
  const ok = names.fresh("ok");
2187
- const result = names.fresh("result");
2534
+ const result2 = names.fresh("result");
2188
2535
  const entryStatements = (0, import_luau_parser2.parse)(`
2189
- local ${ok}, ${result} = xpcall(function()
2190
- return ${G}.require(${(0, import_parser6.luauString)(sources.get(entry).name)})
2536
+ local ${ok}, ${result2} = xpcall(function()
2537
+ return ${G}.require(${(0, import_parser7.luauString)(sources.get(entry).name)})
2191
2538
  end, ${G}.fail)
2192
2539
  if not ${ok} then
2193
- error(${result}, 0)
2540
+ error(${result2}, 0)
2194
2541
  end
2195
- ${modules.get(entry).info.exports ? `return ${result}` : ""}
2542
+ ${modules.get(entry).info.exports ? `return ${result2}` : ""}
2196
2543
  `).body.statements;
2197
2544
  program2.body.statements.push(...entryStatements);
2198
2545
  const map = buildLineMap(program2, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(program2)), origins);
2199
- if (map.size) {
2200
- program2.body.statements.splice(program2.body.statements.length - entryStatements.length, 0, assign(
2546
+ if (map.size || several) {
2547
+ const mapStatements = [assign(
2201
2548
  [member(identifier(G), "lines")],
2202
2549
  [(0, import_luau_parser2.parseExpressionFromSource)(lineMapSource(map))]
2203
- ));
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);
2204
2553
  }
2205
- return { code: (0, import_luau_parser2.print)(program2) + "\n", modules: moduleNames, diagnostics };
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;
2206
2642
  }
2207
- function runtime(G) {
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})`;
2659
+ }
2660
+ return `
2661
+ require(file)(${G})`;
2662
+ }
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(", ");
2208
2666
  return `
2209
2667
  local ${G}
2210
2668
  ${G} = {
2211
2669
  modules = {},
2670
+ -- Every array the program made, so \`typeof\` can tell one from an object.
2671
+ -- The keys are held weakly: being written down keeps no array alive.
2672
+ arrays = setmetatable({}, { __mode = "k" }),
2212
2673
  -- Filled in just before the entry runs: bundle line -> { file, line }.
2213
2674
  lines = nil,
2214
2675
  -- What an error nothing caught says, in the project's files: the bundle's
@@ -2249,7 +2710,7 @@ ${G} = {
2249
2710
  return message
2250
2711
  end
2251
2712
  local lines = ${G}.lines
2252
- if lines == nil then
2713
+ if lines == nil ${several ? `and #${G}.sources == 0 ` : ""}then
2253
2714
  return message .. (trace or "")
2254
2715
  end
2255
2716
  -- The bundle's name, as a position in a message spells it: where this
@@ -2269,6 +2730,81 @@ ${G} = {
2269
2730
  chunk = info.short_src
2270
2731
  end
2271
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` : `
2272
2808
  if chunk == nil then
2273
2809
  chunk = message:match("^(.-):%d+:")
2274
2810
  end
@@ -2290,8 +2826,9 @@ ${G} = {
2290
2826
  for frame in (trace or ""):gmatch("[^\\n]+") do
2291
2827
  local line = frame:match(prefix .. ":(%d+)")
2292
2828
  local at = line ~= nil and place(line) or nil
2293
- if at ~= nil then
2294
- local name = frame:match("function ([%w_.:]+)")
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 '([^']+)'")
2295
2832
  frames[#frames + 1] = " at " .. at .. (name ~= nil and (" (" .. name .. ")") or "")
2296
2833
  end
2297
2834
  end
@@ -2300,13 +2837,36 @@ ${G} = {
2300
2837
  end
2301
2838
  return mapped .. "\\n" .. table.concat(frames, "\\n")
2302
2839
  end,
2303
- 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,` : ""}
2304
2860
  require = function(name)
2305
2861
  local record = ${G}.records[name]
2306
2862
  if record ~= nil then
2307
2863
  return record.exports
2308
2864
  end
2309
- 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` : ""}
2310
2870
  local initialized = {}
2311
2871
  local exports = setmetatable({}, {
2312
2872
  __index = function(_, key)
@@ -2325,7 +2885,7 @@ ${G} = {
2325
2885
  return source[link[2]]
2326
2886
  end
2327
2887
  if module.stars ~= nil and key ~= "default" then
2328
- for _, star in module.stars do
2888
+ for _, star in ipairs(module.stars) do
2329
2889
  local value = ${G}.require(star)[key]
2330
2890
  if value ~= nil then
2331
2891
  return value
@@ -2350,7 +2910,7 @@ ${G} = {
2350
2910
  }
2351
2911
  function stampOf(file) {
2352
2912
  try {
2353
- const at = (0, import_node_fs2.statSync)(file);
2913
+ const at = (0, import_node_fs3.statSync)(file);
2354
2914
  return `${at.size}:${at.mtimeMs}`;
2355
2915
  } catch {
2356
2916
  return void 0;
@@ -2366,19 +2926,19 @@ function parseFile(file, diagnostics, directives) {
2366
2926
  }
2367
2927
  let text;
2368
2928
  try {
2369
- text = (0, import_node_fs2.readFileSync)(file, "utf8");
2929
+ text = (0, import_node_fs3.readFileSync)(file, "utf8");
2370
2930
  } catch {
2371
2931
  diagnostics.push({ file, message: "Cannot read file", line: 1, column: 1, category: "module" });
2372
2932
  return void 0;
2373
2933
  }
2374
2934
  try {
2375
- const program2 = (0, import_parser6.parse)(text);
2376
- const found = (0, import_parser6.directivesOf)(text);
2935
+ const program2 = (0, import_parser7.parse)(text);
2936
+ const found = (0, import_parser7.directivesOf)(text);
2377
2937
  directives.set(file, found);
2378
2938
  if (stamp !== void 0) parsedFiles.set(file, { stamp, program: program2, text, directives: found });
2379
2939
  return { program: program2, text };
2380
2940
  } catch (error) {
2381
- if (error instanceof import_parser6.ParseError || error instanceof import_parser6.LexError) {
2941
+ if (error instanceof import_parser7.ParseError || error instanceof import_parser7.LexError) {
2382
2942
  const { line, column } = error;
2383
2943
  diagnostics.push({ file, message: error.message, line, column, category: "syntax" });
2384
2944
  return void 0;
@@ -2393,14 +2953,14 @@ var libraryCache = /* @__PURE__ */ new Map();
2393
2953
  function parsedLibrary(file) {
2394
2954
  let stamp;
2395
2955
  try {
2396
- const at = (0, import_node_fs2.statSync)(file);
2956
+ const at = (0, import_node_fs3.statSync)(file);
2397
2957
  stamp = `${at.size}:${at.mtimeMs}`;
2398
2958
  } catch {
2399
2959
  stamp = "";
2400
2960
  }
2401
2961
  const cached = libraryCache.get(file);
2402
2962
  if (cached && cached.stamp === stamp) return cached.program;
2403
- const program2 = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(file, "utf8"));
2963
+ const program2 = (0, import_parser7.parse)((0, import_node_fs3.readFileSync)(file, "utf8"));
2404
2964
  libraryCache.set(file, { stamp, program: program2 });
2405
2965
  return program2;
2406
2966
  }
@@ -2408,9 +2968,11 @@ var moduleAnalyses = /* @__PURE__ */ new Map();
2408
2968
  function analyzeModules(modules, config) {
2409
2969
  const out = [];
2410
2970
  const analyses = /* @__PURE__ */ new Map();
2411
- const libraries = config ? (0, import_parser6.resolveTypeLibraries)(config) : { files: [], lowerings: [], problems: [] };
2971
+ const libraries = config ? (0, import_parser7.resolveTypeLibraries)(config) : { files: [], lowerings: [], problems: [] };
2412
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" });
2413
2973
  const libs = libraries.files.map(parsedLibrary);
2974
+ const noLib = config?.noLib ?? false;
2975
+ const checked = libs.length > 0 || !noLib;
2414
2976
  const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
2415
2977
  const byFile = new Map(modules.map((m) => [m.file, m]));
2416
2978
  const exportsCache = /* @__PURE__ */ new Map();
@@ -2423,49 +2985,50 @@ function analyzeModules(modules, config) {
2423
2985
  const done = analyzed.get(file);
2424
2986
  if (done) return done;
2425
2987
  const remembered = moduleAnalyses.get(file);
2426
- if (remembered && remembered.setting === settingFor && libs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === libs[i]) && remembered.program === program2 && [...remembered.read].every(([read2, stamp]) => stampOf(read2) === stamp)) {
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)) {
2427
2989
  analyzed.set(file, remembered.result);
2428
2990
  readWhile.set(file, remembered.read);
2429
2991
  return remembered.result;
2430
2992
  }
2431
- const scopes = (0, import_parser6.analyzeScopes)(program2, { builtinGlobals: globals, reportUndeclared: libs.length > 0 });
2993
+ const scopes = (0, import_parser7.analyzeScopes)(program2, { builtinGlobals: globals, reportUndeclared: checked, noLib });
2432
2994
  const said = [];
2433
2995
  for (const d of scopes.diagnostics) {
2434
2996
  if (d.kind !== "undeclared") continue;
2435
2997
  said.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "type" });
2436
2998
  }
2437
2999
  const before = partials;
2438
- const types = (0, import_parser6.analyzeTypes)(program2, scopes, {
3000
+ const types = (0, import_parser7.analyzeTypes)(program2, scopes, {
2439
3001
  libs,
2440
3002
  resolveModule: resolverFor(file),
2441
- reportUnknownTypes: libs.length > 0
3003
+ noLib,
3004
+ reportUnknownTypes: checked
2442
3005
  });
2443
3006
  for (const d of types.diagnostics) {
2444
3007
  const at = d.node;
2445
3008
  said.push({ file, message: d.message, line: at.line.start, column: at.column.start, category: "type" });
2446
3009
  }
2447
- const result = { types, diagnostics: said };
3010
+ const result2 = { types, diagnostics: said };
2448
3011
  if (partials === before) {
2449
- analyzed.set(file, result);
2450
- const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
3012
+ analyzed.set(file, result2);
3013
+ const read3 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
2451
3014
  const own = stampOf(file);
2452
- if (own !== void 0) read2.set(file, own);
2453
- readWhile.set(file, read2);
2454
- if ([...read2.values()].every((stamp) => stamp !== void 0)) {
2455
- moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program: program2, read: read2, result });
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 });
2456
3019
  }
2457
3020
  }
2458
- return result;
3021
+ return result2;
2459
3022
  };
2460
3023
  const resolverFor = (file) => (specifier) => {
2461
- const target = (0, import_parser6.resolveModulePath)(file, specifier, config);
3024
+ const target = (0, import_parser7.resolveModulePath)(file, specifier, config);
2462
3025
  if (!target) return void 0;
2463
3026
  const noteRead = () => {
2464
- const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
2465
- readWhile.set(file, read2);
3027
+ const read3 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
3028
+ readWhile.set(file, read3);
2466
3029
  const stamp = stampOf(target);
2467
- if (stamp !== void 0) read2.set(target, stamp);
2468
- for (const [below, belowStamp] of readWhile.get(target) ?? []) read2.set(below, belowStamp);
3030
+ if (stamp !== void 0) read3.set(target, stamp);
3031
+ for (const [below, belowStamp] of readWhile.get(target) ?? []) read3.set(below, belowStamp);
2469
3032
  };
2470
3033
  if (inProgress.has(target)) {
2471
3034
  partials++;
@@ -2479,16 +3042,16 @@ function analyzeModules(modules, config) {
2479
3042
  let program2 = byFile.get(target)?.program;
2480
3043
  if (!program2) {
2481
3044
  try {
2482
- program2 = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(target, "utf8"));
3045
+ program2 = (0, import_parser7.parse)((0, import_node_fs3.readFileSync)(target, "utf8"));
2483
3046
  } catch {
2484
3047
  return void 0;
2485
3048
  }
2486
3049
  }
2487
3050
  inProgress.add(target);
2488
3051
  try {
2489
- const scopes = (0, import_parser6.analyzeScopes)(program2, { builtinGlobals: globals });
2490
- const types = byFile.has(target) ? analyzeModule(target, program2).types : (0, import_parser6.analyzeTypes)(program2, scopes, { libs, resolveModule: resolverFor(target), diagnostics: false });
2491
- const exports2 = (0, import_parser6.moduleExports)(program2, scopes, types, resolverFor(target));
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));
2492
3055
  exportsCache.set(target, exports2);
2493
3056
  noteRead();
2494
3057
  return exports2;
@@ -2521,37 +3084,39 @@ function allStatements(statements) {
2521
3084
  }
2522
3085
 
2523
3086
  // src/compile.ts
2524
- var import_node_fs3 = require("fs");
3087
+ var import_node_fs4 = require("fs");
2525
3088
  var import_node_path3 = require("path");
2526
- var import_parser7 = require("@tilua/parser");
3089
+ var import_parser8 = require("@tilua/parser");
2527
3090
  var import_luau_parser3 = require("luau-parser");
2528
3091
  async function compile(source, config) {
2529
3092
  let program2;
2530
3093
  try {
2531
- program2 = (0, import_parser7.parse)(source);
3094
+ program2 = (0, import_parser8.parse)(source);
2532
3095
  } catch (error) {
2533
- if (error instanceof import_parser7.ParseError || error instanceof import_parser7.LexError) {
3096
+ if (error instanceof import_parser8.ParseError || error instanceof import_parser8.LexError) {
2534
3097
  const { line, column } = error;
2535
3098
  return { diagnostics: [{ message: error.message, line, column }] };
2536
3099
  }
2537
3100
  throw error;
2538
3101
  }
2539
- const scopes = (0, import_parser7.analyzeScopes)(program2);
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 });
2540
3105
  const diagnostics = scopes.diagnostics.map((d) => ({
2541
3106
  message: d.message,
2542
3107
  line: d.node.line.start,
2543
3108
  column: d.node.column.start
2544
3109
  }));
2545
- const { config: resolved, problems } = resolveConfig(config, (0, import_node_path3.resolve)("main.tilua"));
2546
- const libraries = resolved ? (0, import_parser7.resolveTypeLibraries)(resolved) : { files: [], lowerings: [], problems: [] };
3110
+ const libraries = resolved ? (0, import_parser8.resolveTypeLibraries)(resolved) : { files: [], lowerings: [], problems: [] };
2547
3111
  const { lowerings, problems: loweringProblems } = await loadLowerings(libraries.lowerings);
2548
3112
  for (const p of [...problems, ...libraries.problems]) {
2549
3113
  diagnostics.push({ message: p.message, line: p.line ?? 1, column: p.column ?? 1 });
2550
3114
  }
2551
3115
  for (const p of loweringProblems) diagnostics.push({ message: p.message, line: 1, column: 1 });
2552
- const libs = libraries.files.map((file) => (0, import_parser7.parse)((0, import_node_fs3.readFileSync)(file, "utf8")));
3116
+ const libs = libraries.files.map((file) => (0, import_parser8.parse)((0, import_node_fs4.readFileSync)(file, "utf8")));
2553
3117
  const lowered = lower(program2, scopes, {
2554
- types: (0, import_parser7.analyzeTypes)(program2, scopes, { libs, diagnostics: false }),
3118
+ types: (0, import_parser8.analyzeTypes)(program2, scopes, { libs, noLib, diagnostics: false }),
3119
+ noLib,
2555
3120
  lowerings,
2556
3121
  target: resolved?.target,
2557
3122
  source