@tilua/compiler 2.4.0 → 3.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/dist/cli.cjs CHANGED
@@ -1,12 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
 
4
+ // node_modules/tsup/assets/cjs_shims.js
5
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
6
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
7
+
4
8
  // src/cli.ts
5
- var import_node_fs2 = require("fs");
9
+ var import_node_fs3 = require("fs");
6
10
  var import_node_path3 = require("path");
7
11
 
8
12
  // src/bundle.ts
9
- var import_node_fs = require("fs");
13
+ var import_node_fs2 = require("fs");
10
14
  var import_node_path2 = require("path");
11
15
  var import_parser3 = require("@tilua/parser");
12
16
  var import_luau_parser2 = require("luau-parser");
@@ -34,10 +38,10 @@ var nil = () => ({ type: "NilLiteral", ...span() });
34
38
  var boolean = (value) => ({ type: "BooleanLiteral", value, ...span() });
35
39
  var number = (value, raw = String(value)) => ({ type: "NumberLiteral", value, raw, ...span() });
36
40
  var string = (value) => ({ type: "StringLiteral", value, raw: "", ...span() });
37
- var member = (object, property) => isLuauName(property) ? { type: "MemberExpression", object, property: identifier(property), ...span() } : index(object, string(property));
38
- var index = (object, key) => ({ type: "IndexExpression", object, index: key, ...span() });
41
+ var member = (object2, property) => isLuauName(property) ? { type: "MemberExpression", object: object2, property: identifier(property), ...span() } : index(object2, string(property));
42
+ var index = (object2, key) => ({ type: "IndexExpression", object: object2, index: key, ...span() });
39
43
  var call = (callee, args2) => ({ type: "CallExpression", callee, arguments: args2, ...span() });
40
- var methodCall = (object, method, args2) => ({ type: "MethodCallExpression", object, method: identifier(method), arguments: args2, ...span() });
44
+ var methodCall = (object2, method, args2) => ({ type: "MethodCallExpression", object: object2, method: identifier(method), arguments: args2, ...span() });
41
45
  var functionExpression = (func) => ({ type: "FunctionExpression", func, ...span() });
42
46
  var binary = (operator, left, right) => ({ type: "BinaryExpression", operator, left, right, ...span() });
43
47
  var unary = (operator, argument) => ({ type: "UnaryExpression", operator, argument, ...span() });
@@ -152,6 +156,62 @@ var Names = class _Names {
152
156
  }
153
157
  };
154
158
 
159
+ // src/language.ts
160
+ var import_node_fs = require("fs");
161
+ var read = (name) => (0, import_node_fs.readFileSync)(new URL(`../runtime/${name}`, importMetaUrl), "utf8");
162
+ var implemented = (source) => new Set([...source.matchAll(/function __NAME__\.(\w+)/g)].map((m) => m[1]));
163
+ var array = read("array.luau");
164
+ var string2 = read("string.luau");
165
+ var object = read("object.luau");
166
+ var arrayMethods = implemented(array);
167
+ var stringMethods = implemented(string2);
168
+ var objectMethods = implemented(object);
169
+ function every(type, is, seen = /* @__PURE__ */ new Set()) {
170
+ if (!type || seen.has(type)) return false;
171
+ seen.add(type);
172
+ if (type.kind === "union") {
173
+ return type.types.filter((m) => !(m.kind === "primitive" && m.name === "nil")).every((m) => every(m, is, seen));
174
+ }
175
+ if (type.kind === "typeParam") return every(type.constraint, is, seen);
176
+ return is(type);
177
+ }
178
+ var isArray = (type) => every(type, (t) => t.kind === "array" || t.kind === "tuple");
179
+ var isString = (type) => every(type, (t) => t.kind === "primitive" && t.name === "string" || t.kind === "literal" && t.base === "string" || t.kind === "templateLiteral");
180
+ function objectKeys(type) {
181
+ const members = type?.kind === "union" ? type.types : type ? [type] : [];
182
+ const orders = [];
183
+ let open = false;
184
+ for (const member2 of members) {
185
+ const keys = [];
186
+ for (const part of member2.kind === "intersection" ? member2.types : [member2]) {
187
+ if (part.kind !== "object") continue;
188
+ for (const key of part.properties.keys()) if (!keys.includes(key)) keys.push(key);
189
+ if (part.indexer) open = true;
190
+ }
191
+ orders.push(keys);
192
+ }
193
+ const order = JSON.stringify(orders[0] ?? []);
194
+ const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) => JSON.stringify(key)).join(", ")} }` : "nil";
195
+ return { known, open: String(open) };
196
+ }
197
+ var plugin = {
198
+ runtime: { array, string: string2, object },
199
+ methodCall({ method, receiver, use }) {
200
+ if (isString(receiver)) {
201
+ return stringMethods.has(method) ? { callee: `${use("string")}.${method}` } : void 0;
202
+ }
203
+ if (isArray(receiver)) {
204
+ return arrayMethods.has(method) ? { callee: `${use("array")}.${method}` } : void 0;
205
+ }
206
+ if (objectMethods.has(method)) {
207
+ const keys = objectKeys(receiver);
208
+ return { callee: `${use("object")}.${method}`, prepend: [keys.known, keys.open] };
209
+ }
210
+ return void 0;
211
+ }
212
+ };
213
+ var languageLowering = { plugin, from: "tilua" };
214
+
155
215
  // src/lower.ts
156
216
  function lower(program, scopes, options = {}) {
157
217
  return new Lowerer(program, scopes, options).run();
@@ -335,21 +395,34 @@ var Lowerer = class {
335
395
  if (id === void 0) return node.name;
336
396
  return this.scopes.bindings.get(id)?.kind === "global" ? node.name : void 0;
337
397
  }
338
- /** What a library says `receiver:method(...)` is. The last library
339
- * loaded is asked first, so a project's own library can answer for a
340
- * method an earlier one also claims; no answer leaves an ordinary Luau
341
- * method call, which is what a value that answers to the method itself
342
- * wants (`text:upper()`). */
398
+ /** The language's own lowering, then each library's, in load order. A
399
+ * runtime is named by its lowering's place here (`loweringRuntime`). */
400
+ get lowerings() {
401
+ return this.allLowerings ??= [languageLowering, ...this.options.lowerings ?? []];
402
+ }
403
+ allLowerings;
404
+ /** What `receiver:method(...)` becomes, when Luau alone would not run it.
405
+ *
406
+ * A method the receiver's metatable gave it — the analyzer says so, and
407
+ * whose metatable it was (`methodSources`) — is asked of whoever declared
408
+ * that metatable, and only them: the language lowers its own
409
+ * (`names:filter(f)` becomes a call into its runtime), a library its own,
410
+ * and one the program declared is taken at its word. Any other method is
411
+ * a member of the receiver's own, which a library may still claim
412
+ * (`console:log`), the last library loaded first. No answer leaves an
413
+ * ordinary Luau method call: `text:upper()` reaches Lua's own. */
343
414
  loweredMethodCall(node) {
344
- const lowerings = this.options.lowerings;
345
- if (!lowerings?.some((l) => l.plugin.methodCall)) return void 0;
415
+ const lowerings = this.lowerings;
416
+ const source = this.options.types?.methodSources.get(node);
417
+ 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
+ if (!asked.some((i) => lowerings[i].plugin.methodCall)) return void 0;
346
419
  const receiver = this.options.types?.typeOf.get(node.object);
347
420
  const receiverGlobal = this.globalName(node.object);
348
421
  const info = this.lazyArguments(node.arguments);
349
- for (let i = lowerings.length - 1; i >= 0; i--) {
350
- const plugin = lowerings[i].plugin;
351
- if (!plugin.methodCall) continue;
352
- const answer = plugin.methodCall({
422
+ for (const i of asked) {
423
+ const plugin2 = lowerings[i].plugin;
424
+ if (!plugin2.methodCall) continue;
425
+ const answer = plugin2.methodCall({
353
426
  method: node.method.name,
354
427
  receiver,
355
428
  receiverGlobal,
@@ -372,15 +445,15 @@ var Lowerer = class {
372
445
  }
373
446
  /** What a library says a call to a global — `print(x)` — is. */
374
447
  loweredGlobalCall(node) {
375
- const lowerings = this.options.lowerings;
448
+ const lowerings = this.lowerings;
376
449
  if (!lowerings?.some((l) => l.plugin.globalCall)) return void 0;
377
450
  const name = this.globalName(node.callee);
378
451
  if (name === void 0) return void 0;
379
452
  const info = this.lazyArguments(node.arguments);
380
453
  for (let i = lowerings.length - 1; i >= 0; i--) {
381
- const plugin = lowerings[i].plugin;
382
- if (!plugin.globalCall) continue;
383
- const answer = plugin.globalCall({
454
+ const plugin2 = lowerings[i].plugin;
455
+ if (!plugin2.globalCall) continue;
456
+ const answer = plugin2.globalCall({
384
457
  name,
385
458
  at: this.callSite(node.callee),
386
459
  get arguments() {
@@ -394,7 +467,7 @@ var Lowerer = class {
394
467
  }
395
468
  /** What a library says a global read as a value — `local p = print` — is. */
396
469
  loweredGlobalValue(node) {
397
- const lowerings = this.options.lowerings;
470
+ const lowerings = this.lowerings;
398
471
  if (!lowerings?.some((l) => l.plugin.globalValue)) return void 0;
399
472
  const name = this.globalName(node);
400
473
  if (name === void 0) return void 0;
@@ -424,7 +497,7 @@ var Lowerer = class {
424
497
  try {
425
498
  return (0, import_luau_parser.parseExpressionFromSource)(source);
426
499
  } catch (error) {
427
- this.report(node, `'${this.options.lowerings[index2].from}' lowered this to Luau that does not parse: ${JSON.stringify(source)} (${error.message})`);
500
+ this.report(node, `'${this.lowerings[index2].from}' lowered this to Luau that does not parse: ${JSON.stringify(source)} (${error.message})`);
428
501
  return void 0;
429
502
  }
430
503
  }
@@ -445,8 +518,8 @@ var Lowerer = class {
445
518
  ], true)));
446
519
  }
447
520
  for (const { index: index2, runtime: runtime2, name } of this.loweringUsed) {
448
- const { plugin, from } = this.options.lowerings[index2];
449
- const source = plugin.runtime?.[runtime2];
521
+ const { plugin: plugin2, from } = this.lowerings[index2];
522
+ const source = plugin2.runtime?.[runtime2];
450
523
  if (source === void 0) {
451
524
  this.diagnostics.push({
452
525
  message: `'${from}' asked for a runtime it does not have: '${runtime2}'`,
@@ -781,6 +854,7 @@ var Lowerer = class {
781
854
  case "ExportTypeAliasStatement":
782
855
  case "DeclareStatement":
783
856
  case "DeclareClassStatement":
857
+ case "DeclareMetatableStatement":
784
858
  case "ErrorStatement":
785
859
  return [];
786
860
  case "VariableDeclaration":
@@ -1381,11 +1455,11 @@ end
1381
1455
  callValues(node) {
1382
1456
  const chain = optionalChain(node);
1383
1457
  if (chain) return this.optionalChainExpression(chain);
1384
- const object = linkObject(node);
1385
- if (node.type === "CallExpression" && object.type === "Identifier") {
1386
- return this.link(node, this.reference(object));
1458
+ const object2 = linkObject(node);
1459
+ if (node.type === "CallExpression" && object2.type === "Identifier") {
1460
+ return this.link(node, this.reference(object2));
1387
1461
  }
1388
- return this.link(node, this.expression(object));
1462
+ return this.link(node, this.expression(object2));
1389
1463
  }
1390
1464
  /** A call is one value in tilua, and Luau would hand on every value one
1391
1465
  * returns in the last place of an argument list or a `return`: it is
@@ -1424,16 +1498,16 @@ end
1424
1498
  }))];
1425
1499
  }
1426
1500
  /** One link of an access chain, read from `object`. */
1427
- link(node, object) {
1501
+ link(node, object2) {
1428
1502
  switch (node.type) {
1429
1503
  case "MemberExpression":
1430
- return member(object, node.property.name);
1504
+ return member(object2, node.property.name);
1431
1505
  case "IndexExpression":
1432
- return index(object, this.expression(node.index));
1506
+ return index(object2, this.expression(node.index));
1433
1507
  case "CallExpression": {
1434
1508
  const lowered = this.loweredGlobalCall(node);
1435
1509
  const args2 = this.arguments(node.arguments);
1436
- if (!lowered) return call(object, args2);
1510
+ if (!lowered) return call(object2, args2);
1437
1511
  return call(calleePath(lowered.callee), [...lowered.prepend, ...args2]);
1438
1512
  }
1439
1513
  case "MethodCallExpression": {
@@ -1442,14 +1516,14 @@ end
1442
1516
  if (lowered) {
1443
1517
  return call(calleePath(lowered.callee), [
1444
1518
  ...lowered.prepend,
1445
- ...lowered.passReceiver ? [object] : [],
1519
+ ...lowered.passReceiver ? [object2] : [],
1446
1520
  ...args2
1447
1521
  ]);
1448
1522
  }
1449
1523
  if (!isLuauName(node.method.name)) {
1450
1524
  this.report(node.method, `'${node.method.name}' is a Luau keyword and cannot be called with ':'`);
1451
1525
  }
1452
- return methodCall(object, node.method.name, args2);
1526
+ return methodCall(object2, node.method.name, args2);
1453
1527
  }
1454
1528
  }
1455
1529
  }
@@ -1470,8 +1544,8 @@ end
1470
1544
  }
1471
1545
  return out2;
1472
1546
  }
1473
- applySegment(segment, object) {
1474
- return segment.reduce((value, link) => this.link(link, value), object);
1547
+ applySegment(segment, object2) {
1548
+ return segment.reduce((value, link) => this.link(link, value), object2);
1475
1549
  }
1476
1550
  /** `a?.b:m()` as a statement:
1477
1551
  * `do local ref = a if ref ~= nil then ref.b:m() end end` */
@@ -1547,9 +1621,9 @@ end
1547
1621
  const before = [];
1548
1622
  let target;
1549
1623
  if (node.target.type === "MemberExpression" || node.target.type === "IndexExpression") {
1550
- const object = this.expression(node.target.object);
1624
+ const object2 = this.expression(node.target.object);
1551
1625
  const objectName = this.names.fresh("target");
1552
- before.push(local([objectName], [object]));
1626
+ before.push(local([objectName], [object2]));
1553
1627
  if (node.target.type === "MemberExpression") {
1554
1628
  target = member(identifier(objectName), node.target.property.name);
1555
1629
  } else {
@@ -1967,15 +2041,15 @@ async function loadLowerings(modules) {
1967
2041
  for (const module2 of modules) {
1968
2042
  try {
1969
2043
  const loaded = await import((0, import_node_url.pathToFileURL)(module2.file).href);
1970
- const plugin = loaded.default;
1971
- if (!plugin || typeof plugin !== "object") {
2044
+ const plugin2 = loaded.default;
2045
+ if (!plugin2 || typeof plugin2 !== "object") {
1972
2046
  problems.push({
1973
2047
  file: module2.file,
1974
2048
  message: `'${module2.from}' lowering module has no default export`
1975
2049
  });
1976
2050
  continue;
1977
2051
  }
1978
- lowerings.push({ plugin, from: module2.from });
2052
+ lowerings.push({ plugin: plugin2, from: module2.from, library: module2.library });
1979
2053
  } catch (error) {
1980
2054
  problems.push({
1981
2055
  file: module2.file,
@@ -2227,7 +2301,7 @@ ${G} = {
2227
2301
  }
2228
2302
  function stampOf(file) {
2229
2303
  try {
2230
- const at = (0, import_node_fs.statSync)(file);
2304
+ const at = (0, import_node_fs2.statSync)(file);
2231
2305
  return `${at.size}:${at.mtimeMs}`;
2232
2306
  } catch {
2233
2307
  return void 0;
@@ -2243,7 +2317,7 @@ function parseFile(file, diagnostics, directives) {
2243
2317
  }
2244
2318
  let text;
2245
2319
  try {
2246
- text = (0, import_node_fs.readFileSync)(file, "utf8");
2320
+ text = (0, import_node_fs2.readFileSync)(file, "utf8");
2247
2321
  } catch {
2248
2322
  diagnostics.push({ file, message: "Cannot read file", line: 1, column: 1, category: "module" });
2249
2323
  return void 0;
@@ -2270,14 +2344,14 @@ var libraryCache = /* @__PURE__ */ new Map();
2270
2344
  function parsedLibrary(file) {
2271
2345
  let stamp;
2272
2346
  try {
2273
- const at = (0, import_node_fs.statSync)(file);
2347
+ const at = (0, import_node_fs2.statSync)(file);
2274
2348
  stamp = `${at.size}:${at.mtimeMs}`;
2275
2349
  } catch {
2276
2350
  stamp = "";
2277
2351
  }
2278
2352
  const cached = libraryCache.get(file);
2279
2353
  if (cached && cached.stamp === stamp) return cached.program;
2280
- const program = (0, import_parser3.parse)((0, import_node_fs.readFileSync)(file, "utf8"));
2354
+ const program = (0, import_parser3.parse)((0, import_node_fs2.readFileSync)(file, "utf8"));
2281
2355
  libraryCache.set(file, { stamp, program });
2282
2356
  return program;
2283
2357
  }
@@ -2300,7 +2374,7 @@ function analyzeModules(modules, config2) {
2300
2374
  const done = analyzed.get(file);
2301
2375
  if (done) return done;
2302
2376
  const remembered = moduleAnalyses.get(file);
2303
- if (remembered && remembered.setting === settingFor && libs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === libs[i]) && remembered.program === program && [...remembered.read].every(([read, stamp]) => stampOf(read) === stamp)) {
2377
+ if (remembered && remembered.setting === settingFor && libs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === libs[i]) && remembered.program === program && [...remembered.read].every(([read2, stamp]) => stampOf(read2) === stamp)) {
2304
2378
  analyzed.set(file, remembered.result);
2305
2379
  readWhile.set(file, remembered.read);
2306
2380
  return remembered.result;
@@ -2324,12 +2398,12 @@ function analyzeModules(modules, config2) {
2324
2398
  const result = { types, diagnostics: said };
2325
2399
  if (partials === before) {
2326
2400
  analyzed.set(file, result);
2327
- const read = readWhile.get(file) ?? /* @__PURE__ */ new Map();
2401
+ const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
2328
2402
  const own = stampOf(file);
2329
- if (own !== void 0) read.set(file, own);
2330
- readWhile.set(file, read);
2331
- if ([...read.values()].every((stamp) => stamp !== void 0)) {
2332
- moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program, read, result });
2403
+ if (own !== void 0) read2.set(file, own);
2404
+ readWhile.set(file, read2);
2405
+ if ([...read2.values()].every((stamp) => stamp !== void 0)) {
2406
+ moduleAnalyses.set(file, { setting: settingFor, libs: [...libs], program, read: read2, result });
2333
2407
  }
2334
2408
  }
2335
2409
  return result;
@@ -2338,11 +2412,11 @@ function analyzeModules(modules, config2) {
2338
2412
  const target = (0, import_parser3.resolveModulePath)(file, specifier, config2);
2339
2413
  if (!target) return void 0;
2340
2414
  const noteRead = () => {
2341
- const read = readWhile.get(file) ?? /* @__PURE__ */ new Map();
2342
- readWhile.set(file, read);
2415
+ const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
2416
+ readWhile.set(file, read2);
2343
2417
  const stamp = stampOf(target);
2344
- if (stamp !== void 0) read.set(target, stamp);
2345
- for (const [below, belowStamp] of readWhile.get(target) ?? []) read.set(below, belowStamp);
2418
+ if (stamp !== void 0) read2.set(target, stamp);
2419
+ for (const [below, belowStamp] of readWhile.get(target) ?? []) read2.set(below, belowStamp);
2346
2420
  };
2347
2421
  if (inProgress.has(target)) {
2348
2422
  partials++;
@@ -2356,7 +2430,7 @@ function analyzeModules(modules, config2) {
2356
2430
  let program = byFile.get(target)?.program;
2357
2431
  if (!program) {
2358
2432
  try {
2359
- program = (0, import_parser3.parse)((0, import_node_fs.readFileSync)(target, "utf8"));
2433
+ program = (0, import_parser3.parse)((0, import_node_fs2.readFileSync)(target, "utf8"));
2360
2434
  } catch {
2361
2435
  return void 0;
2362
2436
  }
@@ -2436,7 +2510,7 @@ async function main(from) {
2436
2510
  process.exit(1);
2437
2511
  }
2438
2512
  const target = out ?? from.replace(/\.tilua$/, "") + ".luau";
2439
- (0, import_node_fs2.writeFileSync)(target, result.code);
2513
+ (0, import_node_fs3.writeFileSync)(target, result.code);
2440
2514
  console.log(`${target}: ${result.modules.length} module${result.modules.length === 1 ? "" : "s"}`);
2441
2515
  process.exit(result.diagnostics.length ? 1 : 0);
2442
2516
  }
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  bundle
4
- } from "./chunk-3ZQ3EITC.js";
4
+ } from "./chunk-X4DM2BX2.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { writeFileSync } from "fs";