@tilua/compiler 1.0.1 → 1.1.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
@@ -137,144 +137,6 @@ function isLuauName(name) {
137
137
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && !LUAU_KEYWORDS.has(name);
138
138
  }
139
139
 
140
- // src/console.ts
141
- var CONSOLE_RUNTIME = `
142
- local __NAME__ = (function()
143
- local console = {}
144
- local MAX_DEPTH = 4
145
- local ESCAPES = { ["\\n"] = "\\\\n", ["\\r"] = "\\\\r", ["\\t"] = "\\\\t", ["\\""] = "\\\\\\"", ["\\\\"] = "\\\\\\\\" }
146
-
147
- local function quote(text)
148
- return '"' .. text:gsub('[%c"\\\\]', function(c) return ESCAPES[c] or ("\\\\%d"):format(c:byte()) end) .. '"'
149
- end
150
-
151
- -- A table with 1..n and nothing else prints as a list, as it was written.
152
- local function isArray(value)
153
- local count = 0
154
- for key in pairs(value) do
155
- if type(key) ~= "number" then return false end
156
- count = count + 1
157
- end
158
- return count == #value
159
- end
160
-
161
- local function isIdentifier(key)
162
- return type(key) == "string" and key:match("^[A-Za-z_][A-Za-z0-9_]*$") ~= nil
163
- end
164
-
165
- local render
166
-
167
- local function renderTable(value, meta, seen, depth, indent)
168
- if seen[value] then return "<cycle>" end
169
- if depth > MAX_DEPTH then return "..." end
170
- seen[value] = true
171
- local inner = indent .. " "
172
- local parts = {}
173
- if isArray(value) then
174
- for i = 1, #value do
175
- parts[#parts + 1] = inner .. render(value[i], nil, seen, depth + 1, inner)
176
- end
177
- else
178
- local keys = {}
179
- for key in pairs(value) do keys[#keys + 1] = key end
180
- table.sort(keys, function(a, b) return tostring(a) < tostring(b) end)
181
- for _, key in ipairs(keys) do
182
- local name = isIdentifier(key) and key or ("[" .. render(key, nil, seen, depth + 1, inner) .. "]")
183
- parts[#parts + 1] = inner .. name .. " = " .. render(value[key], nil, seen, depth + 1, inner)
184
- end
185
- end
186
- seen[value] = nil
187
- if #parts == 0 then return "{}" end
188
- local open = isArray(value) and "[" or "{"
189
- local close = isArray(value) and "]" or "}"
190
- return open .. "\\n" .. table.concat(parts, ",\\n") .. "\\n" .. indent .. close
191
- end
192
-
193
- -- \`meta\` is what the compiler knew about this value and the runtime cannot:
194
- -- a function's type, and the code it was written as.
195
- render = function(value, meta, seen, depth, indent)
196
- local kind = type(value)
197
- if kind == "string" then return quote(value) end
198
- if kind == "number" or kind == "boolean" or kind == "nil" then return tostring(value) end
199
- if kind == "function" then
200
- if meta == nil then return "function" end
201
- if meta[2] == nil or meta[2] == "" then return meta[1] end
202
- return meta[1] .. " " .. meta[2]
203
- end
204
- if kind == "table" then
205
- local shown = rawget(value, "__tostring") == nil and getmetatable(value)
206
- -- A value with its own \`__tostring\` (a Roblox Vector3, a class
207
- -- instance that says how it reads) is trusted to describe itself.
208
- if shown ~= nil and rawget(shown, "__tostring") ~= nil then return tostring(value) end
209
- return renderTable(value, meta, seen, depth, indent)
210
- end
211
- return tostring(value)
212
- end
213
-
214
- local function describe(meta, index, value)
215
- return render(value, meta ~= nil and meta[index] or nil, {}, 1, "")
216
- end
217
-
218
- local function join(meta, count, ...)
219
- local parts = {}
220
- for i = 1, count do
221
- parts[#parts + 1] = describe(meta, i, (select(i, ...)))
222
- end
223
- return table.concat(parts, " ")
224
- end
225
-
226
- -- A line of the bundle, as the place in the project that wrote it.
227
- local lines = __LINES__
228
- local function place(line)
229
- local origin = lines ~= nil and lines[tonumber(line)]
230
- if origin == nil then return nil end
231
- return origin[1] .. ":" .. tostring(origin[2])
232
- end
233
-
234
- -- Luau names the bundle in every frame; the project's files are what the
235
- -- reader knows. Frames the map has nothing for are dropped: they are this
236
- -- runtime's own, and the bundle's.
237
- local function trace(level)
238
- local raw = debug.traceback(nil, level)
239
- local out = {}
240
- for frame in raw:gmatch("[^\\n]+") do
241
- local line = frame:match(":(%d+)")
242
- local at = line ~= nil and place(line) or nil
243
- if at ~= nil then
244
- local name = frame:match("function ([%w_.:]+)")
245
- out[#out + 1] = " at " .. at .. (name ~= nil and (" (" .. name .. ")") or "")
246
- end
247
- end
248
- return table.concat(out, "\\n")
249
- end
250
-
251
- -- Never on the caller's thread: reporting a problem must not become one.
252
- local detach = (task ~= nil and task.spawn) or function(f) return f() end
253
-
254
- local function raise(prefix, meta, count, ...)
255
- local message = prefix .. join(meta, count, ...)
256
- local where = trace(3)
257
- detach(function()
258
- error(where ~= "" and (message .. "\\n" .. where) or message, 0)
259
- end)
260
- end
261
-
262
- function console.log(meta, ...)
263
- print(join(meta, select("#", ...), ...))
264
- end
265
-
266
- function console.warn(meta, ...)
267
- raise("[warn] ", meta, select("#", ...), ...)
268
- end
269
-
270
- function console.error(meta, ...)
271
- raise("", meta, select("#", ...), ...)
272
- end
273
-
274
- return console
275
- end)()
276
- `;
277
-
278
140
  // src/names.ts
279
141
  var Names = class _Names {
280
142
  constructor(taken) {
@@ -422,60 +284,9 @@ var Lowerer = class {
422
284
  }
423
285
  loweringNames = /* @__PURE__ */ new Map();
424
286
  loweringUsed = [];
425
- /** What a library says `receiver:method(...)` is. The last library
426
- * loaded is asked first, so a project's own library can answer for a
427
- * method an earlier one also claims; no answer leaves an ordinary Luau
428
- * method call, which is what a value that answers to the method itself
429
- * wants (`text:upper()`). */
430
- /** `console:log(a, f)` and its two siblings.
431
- *
432
- * Handled here rather than through a library's lowering because what they
433
- * need is not a different callee but *more than was written*: a function
434
- * prints as its type and its code, and neither survives to runtime. The
435
- * compiler is the only thing that has both, so it passes them along:
436
- *
437
- * console:log(a, double)
438
- * -- becomes
439
- * tilua_console.log({ [2] = { "(x: number) => number", "(x: number) => x * 2" } }, a, double)
440
- *
441
- * The meta table is `nil` when nothing was worth saying, which is most
442
- * calls. `console` itself is never passed: it holds nothing. */
443
- consoleCall(node) {
444
- if (node.object.type !== "Identifier" || node.object.name !== "console") return void 0;
445
- const method = node.method.name;
446
- if (method !== "log" && method !== "warn" && method !== "error") return void 0;
447
- const binding = this.scopes.bindingOf.get(node.object);
448
- if (binding !== void 0 && this.scopes.bindings.get(binding)?.kind !== "global") return void 0;
449
- const meta = this.consoleMeta(node.arguments);
450
- return call(
451
- member(this.helper("console"), method),
452
- [meta ?? nil(), ...this.values(node.arguments)]
453
- );
454
- }
455
- /** What the compiler knows about the arguments and the runtime cannot:
456
- * for each function argument, the type it was inferred as and the code it
457
- * was written as. Keyed by position, so an argument that needs nothing
458
- * costs nothing. */
459
- consoleMeta(args) {
460
- const fields = [];
461
- args.forEach((argument, index2) => {
462
- const type = this.options.types?.typeOf.get(argument);
463
- if (!type || !isFunctionType(type)) return;
464
- const written = this.writtenSource(this.declarationOf(argument) ?? argument);
465
- fields.push({
466
- type: "TableFieldComputed",
467
- key: number(index2 + 1),
468
- value: table([
469
- { type: "TableFieldPositional", value: string((0, import_parser2.formatType)(type)) },
470
- { type: "TableFieldPositional", value: string(written) }
471
- ])
472
- });
473
- });
474
- return fields.length ? table(fields) : void 0;
475
- }
476
287
  /** What a name was declared as: the value of `const f = ...`, or the whole
477
- * `function f() { ... }`. Built once, on the first `console` call that
478
- * needs it — most files have none. */
288
+ * `function f() { ... }`. Built once, on the first call a library asks
289
+ * about — most files have none. */
479
290
  declarationOf(argument) {
480
291
  if (argument.type !== "Identifier") return void 0;
481
292
  if (!this.declarations) {
@@ -514,21 +325,127 @@ var Lowerer = class {
514
325
  const trim = (line) => line.startsWith(indent) ? line.slice(indent.length) : line;
515
326
  return [first, ...middle.map(trim), trim(last)].join("\n");
516
327
  }
328
+ /** Where `node` was written, as a library is told it. */
329
+ callSite(node) {
330
+ return { file: this.options.file, line: node.line.start, column: node.column.start };
331
+ }
332
+ /** What the compiler knows about each argument and the runtime cannot:
333
+ * its type, and the code it — or what it names — was written as. Built
334
+ * only if a library reads it. */
335
+ argumentInfo(args) {
336
+ return args.map((argument) => {
337
+ const declaration = this.declarationOf(argument);
338
+ const type = this.options.types?.typeOf.get(argument);
339
+ return {
340
+ type,
341
+ typeText: type ? (0, import_parser2.formatType)(type) : void 0,
342
+ source: this.writtenSource(argument),
343
+ declaration: declaration ? this.writtenSource(declaration) : void 0,
344
+ spread: argument.type === "SpreadElement"
345
+ };
346
+ });
347
+ }
348
+ /** The name a global is read by at `node`, or `undefined` when `node` is
349
+ * not a global — a local of the same name is the author's. */
350
+ globalName(node) {
351
+ if (node.type !== "Identifier") return void 0;
352
+ const id = this.scopes.bindingOf.get(node);
353
+ if (id === void 0) return node.name;
354
+ return this.scopes.bindings.get(id)?.kind === "global" ? node.name : void 0;
355
+ }
356
+ /** What a library says `receiver:method(...)` is. The last library
357
+ * loaded is asked first, so a project's own library can answer for a
358
+ * method an earlier one also claims; no answer leaves an ordinary Luau
359
+ * method call, which is what a value that answers to the method itself
360
+ * wants (`text:upper()`). */
517
361
  loweredMethodCall(node) {
518
362
  const lowerings = this.options.lowerings;
519
- if (!lowerings?.length) return void 0;
363
+ if (!lowerings?.some((l) => l.plugin.methodCall)) return void 0;
520
364
  const receiver = this.options.types?.typeOf.get(node.object);
365
+ const receiverGlobal = this.globalName(node.object);
366
+ const info = this.lazyArguments(node.arguments);
521
367
  for (let i = lowerings.length - 1; i >= 0; i--) {
522
- const answer = lowerings[i].plugin.methodCall?.({
368
+ const plugin = lowerings[i].plugin;
369
+ if (!plugin.methodCall) continue;
370
+ const answer = plugin.methodCall({
523
371
  method: node.method.name,
524
372
  receiver,
373
+ receiverGlobal,
525
374
  argumentCount: node.arguments.length,
375
+ at: this.callSite(node.method),
376
+ get arguments() {
377
+ return info();
378
+ },
526
379
  use: (runtime2) => this.loweringRuntime(i, runtime2)
527
380
  });
528
- if (answer) return { callee: answer.callee, passReceiver: answer.passReceiver !== false };
381
+ if (answer) {
382
+ return {
383
+ callee: answer.callee,
384
+ prepend: this.prepended(answer.prepend, i, node),
385
+ passReceiver: answer.passReceiver !== false
386
+ };
387
+ }
529
388
  }
530
389
  return void 0;
531
390
  }
391
+ /** What a library says a call to a global — `print(x)` — is. */
392
+ loweredGlobalCall(node) {
393
+ const lowerings = this.options.lowerings;
394
+ if (!lowerings?.some((l) => l.plugin.globalCall)) return void 0;
395
+ const name = this.globalName(node.callee);
396
+ if (name === void 0) return void 0;
397
+ const info = this.lazyArguments(node.arguments);
398
+ for (let i = lowerings.length - 1; i >= 0; i--) {
399
+ const plugin = lowerings[i].plugin;
400
+ if (!plugin.globalCall) continue;
401
+ const answer = plugin.globalCall({
402
+ name,
403
+ at: this.callSite(node.callee),
404
+ get arguments() {
405
+ return info();
406
+ },
407
+ use: (runtime2) => this.loweringRuntime(i, runtime2)
408
+ });
409
+ if (answer) return { callee: answer.callee, prepend: this.prepended(answer.prepend, i, node) };
410
+ }
411
+ return void 0;
412
+ }
413
+ /** What a library says a global read as a value — `local p = print` — is. */
414
+ loweredGlobalValue(node) {
415
+ const lowerings = this.options.lowerings;
416
+ if (!lowerings?.some((l) => l.plugin.globalValue)) return void 0;
417
+ const name = this.globalName(node);
418
+ if (name === void 0) return void 0;
419
+ for (let i = lowerings.length - 1; i >= 0; i--) {
420
+ const answer = lowerings[i].plugin.globalValue?.({
421
+ name,
422
+ at: this.callSite(node),
423
+ use: (runtime2) => this.loweringRuntime(i, runtime2)
424
+ });
425
+ if (answer !== void 0) return this.libraryExpression(answer, i, node);
426
+ }
427
+ return void 0;
428
+ }
429
+ lazyArguments(args) {
430
+ let info;
431
+ return () => info ??= this.argumentInfo(args);
432
+ }
433
+ prepended(sources, index2, node) {
434
+ return (sources ?? []).flatMap((source) => {
435
+ const expression = this.libraryExpression(source, index2, node);
436
+ return expression ? [expression] : [];
437
+ });
438
+ }
439
+ /** Luau a library wrote as an expression. One that does not parse is the
440
+ * library's mistake, reported at the call it was written for. */
441
+ libraryExpression(source, index2, node) {
442
+ try {
443
+ return (0, import_luau_parser.parseExpressionFromSource)(source);
444
+ } catch (error) {
445
+ this.report(node, `'${this.options.lowerings[index2].from}' lowered this to Luau that does not parse: ${JSON.stringify(source)} (${error.message})`);
446
+ return void 0;
447
+ }
448
+ }
532
449
  helperDefinitions() {
533
450
  const out = [];
534
451
  const assign2 = this.helpers.get("assign");
@@ -556,12 +473,9 @@ var Lowerer = class {
556
473
  });
557
474
  continue;
558
475
  }
559
- out.push(...(0, import_luau_parser.parse)(source.replace(/__NAME__/g, name)).body.statements);
560
- }
561
- const console = this.helpers.get("console");
562
- if (console) {
563
476
  const lines = this.options.module?.lines ?? "nil";
564
- out.push(...(0, import_luau_parser.parse)(CONSOLE_RUNTIME.replace(/__NAME__/g, console).replace(/__LINES__/g, lines)).body.statements);
477
+ const fail = this.options.module?.fail ?? "nil";
478
+ out.push(...(0, import_luau_parser.parse)(source.replace(/__NAME__/g, name).replace(/__LINES__/g, lines).replace(/__FAIL__/g, fail)).body.statements);
565
479
  }
566
480
  const concat = this.helpers.get("concat");
567
481
  if (concat) {
@@ -1419,7 +1333,7 @@ end
1419
1333
  }
1420
1334
  switch (node.type) {
1421
1335
  case "Identifier":
1422
- return this.reference(node);
1336
+ return this.loweredGlobalValue(node) ?? this.reference(node);
1423
1337
  case "NilLiteral":
1424
1338
  return nil();
1425
1339
  case "BooleanLiteral":
@@ -1468,7 +1382,11 @@ end
1468
1382
  case "MethodCallExpression": {
1469
1383
  const chain = optionalChain(node);
1470
1384
  if (chain) return this.optionalChainExpression(chain);
1471
- return this.link(node, this.expression(linkObject(node)));
1385
+ const object = linkObject(node);
1386
+ if (node.type === "CallExpression" && object.type === "Identifier") {
1387
+ return this.link(node, this.reference(object));
1388
+ }
1389
+ return this.link(node, this.expression(object));
1472
1390
  }
1473
1391
  case "ParenthesizedExpression":
1474
1392
  return parenthesized(this.expression(node.expression));
@@ -1513,15 +1431,21 @@ end
1513
1431
  return member(object, node.property.name);
1514
1432
  case "IndexExpression":
1515
1433
  return index(object, this.expression(node.index));
1516
- case "CallExpression":
1517
- return call(object, this.values(node.arguments));
1518
- case "MethodCallExpression": {
1519
- const consoleCall = this.consoleCall(node);
1520
- if (consoleCall) return consoleCall;
1434
+ case "CallExpression": {
1435
+ const lowered = this.loweredGlobalCall(node);
1521
1436
  const args = this.values(node.arguments);
1437
+ if (lowered) return call(calleePath(lowered.callee), [...lowered.prepend, ...args]);
1438
+ return call(object, args);
1439
+ }
1440
+ case "MethodCallExpression": {
1522
1441
  const lowered = this.loweredMethodCall(node);
1442
+ const args = this.values(node.arguments);
1523
1443
  if (lowered) {
1524
- return call(calleePath(lowered.callee), lowered.passReceiver ? [object, ...args] : args);
1444
+ return call(calleePath(lowered.callee), [
1445
+ ...lowered.prepend,
1446
+ ...lowered.passReceiver ? [object] : [],
1447
+ ...args
1448
+ ]);
1525
1449
  }
1526
1450
  if (!isLuauName(node.method.name)) {
1527
1451
  this.report(node.method, `'${node.method.name}' is a Luau keyword and cannot be called with ':'`);
@@ -1934,10 +1858,6 @@ function mentions(expression, name) {
1934
1858
  walk(expression);
1935
1859
  return found;
1936
1860
  }
1937
- function isFunctionType(type) {
1938
- if (type.kind === "function") return true;
1939
- return type.kind === "intersection" && type.types.some((t) => t.kind === "function");
1940
- }
1941
1861
  function allNodes(root) {
1942
1862
  const out = [];
1943
1863
  const visit = (value) => {
@@ -2061,16 +1981,19 @@ async function bundle(options) {
2061
1981
  const file = pending.shift();
2062
1982
  if (modules.has(file)) continue;
2063
1983
  const source = sources.get(file);
1984
+ const shown = (0, import_node_path2.relative)(root, file).replace(/\\/g, "/");
2064
1985
  const lowered = lower(source.program, source.scopes, {
2065
1986
  names,
2066
1987
  types: analysis.types.get(file),
2067
1988
  lowerings,
2068
1989
  target: options.target ?? config?.target,
2069
1990
  source: source.text,
1991
+ file: shown,
2070
1992
  module: {
2071
1993
  name: source.name,
2072
1994
  require: requireExpression,
2073
1995
  lines: `${G}.lines`,
1996
+ fail: `${G}.fail`,
2074
1997
  resolve: (specifier) => {
2075
1998
  const target = (0, import_parser3.resolveModulePath)(file, specifier, config);
2076
1999
  if (!target || target.endsWith(".d.tilua") || !sources.has(target)) return void 0;
@@ -2080,7 +2003,6 @@ async function bundle(options) {
2080
2003
  }
2081
2004
  });
2082
2005
  for (const d of lowered.diagnostics) diagnostics.push({ file, ...d, category: "module" });
2083
- const shown = (0, import_node_path2.relative)(root, file).replace(/\\/g, "/");
2084
2006
  for (const statement of allStatements(lowered.statements)) {
2085
2007
  const line = lowered.origins.get(statement);
2086
2008
  if (line !== void 0) origins.set(statement, { file: shown, line });
@@ -2130,11 +2052,21 @@ async function bundle(options) {
2130
2052
  fields.push(field("load", functionExpression(functionBody([module2.exportsName], module2.statements, true))));
2131
2053
  modulesTable.fields.push({ type: "TableFieldComputed", key: string(module2.name), value: table(fields) });
2132
2054
  }
2133
- const start = call(requireExpression, [string(sources.get(entry).name)]);
2134
- program2.body.statements.push(modules.get(entry).info.exports ? returns([start]) : callStatement(start));
2055
+ const ok = names.fresh("ok");
2056
+ const result = names.fresh("result");
2057
+ const entryStatements = (0, import_luau_parser2.parse)(`
2058
+ local ${ok}, ${result} = xpcall(function()
2059
+ return ${G}.require(${JSON.stringify(sources.get(entry).name)})
2060
+ end, ${G}.fail)
2061
+ if not ${ok} then
2062
+ error(${result}, 0)
2063
+ end
2064
+ ${modules.get(entry).info.exports ? `return ${result}` : ""}
2065
+ `).body.statements;
2066
+ program2.body.statements.push(...entryStatements);
2135
2067
  const map = buildLineMap(program2, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(program2)), origins);
2136
2068
  if (map.size) {
2137
- program2.body.statements.splice(program2.body.statements.length - 1, 0, assign(
2069
+ program2.body.statements.splice(program2.body.statements.length - entryStatements.length, 0, assign(
2138
2070
  [member(identifier(G), "lines")],
2139
2071
  [(0, import_luau_parser2.parseExpressionFromSource)(lineMapSource(map))]
2140
2072
  ));
@@ -2146,6 +2078,52 @@ function runtime(G) {
2146
2078
  local ${G}
2147
2079
  ${G} = {
2148
2080
  modules = {},
2081
+ -- Filled in just before the entry runs: bundle line -> { file, line }.
2082
+ lines = nil,
2083
+ -- What an error nothing caught says, in the project's files: the bundle's
2084
+ -- own positions in the message are replaced by the places they came from,
2085
+ -- and a traceback of those places follows. A message that is not a string
2086
+ -- is left as it is, and a position that is not this bundle's (a library
2087
+ -- that raised its error with one already mapped) is left alone.
2088
+ fail = function(message)
2089
+ if type(message) ~= "string" then
2090
+ return message
2091
+ end
2092
+ local lines = ${G}.lines
2093
+ if lines == nil then
2094
+ return debug.traceback(message, 2)
2095
+ end
2096
+ local chunk
2097
+ if debug.info ~= nil then
2098
+ chunk = debug.info(1, "s")
2099
+ else
2100
+ chunk = debug.getinfo(1, "S").short_src
2101
+ end
2102
+ local prefix = chunk:gsub("%p", "%%%0")
2103
+ local function place(line)
2104
+ local origin = lines[tonumber(line)]
2105
+ if origin == nil then
2106
+ return nil
2107
+ end
2108
+ return origin[1] .. ":" .. tostring(origin[2])
2109
+ end
2110
+ local mapped = message:gsub(prefix .. ":(%d+)", function(line)
2111
+ return place(line)
2112
+ end)
2113
+ local frames = {}
2114
+ for frame in debug.traceback("", 2):gmatch("[^\\n]+") do
2115
+ local line = frame:match(prefix .. ":(%d+)")
2116
+ local at = line ~= nil and place(line) or nil
2117
+ if at ~= nil then
2118
+ local name = frame:match("function ([%w_.:]+)")
2119
+ frames[#frames + 1] = " at " .. at .. (name ~= nil and (" (" .. name .. ")") or "")
2120
+ end
2121
+ end
2122
+ if #frames == 0 then
2123
+ return mapped
2124
+ end
2125
+ return mapped .. "\\n" .. table.concat(frames, "\\n")
2126
+ end,
2149
2127
  records = {},
2150
2128
  require = function(name)
2151
2129
  local record = ${G}.records[name]
package/dist/index.d.cts CHANGED
@@ -88,9 +88,13 @@ interface ModuleContext {
88
88
  /** The bundle's `require`: an expression that, called with a module's
89
89
  * name, returns its exports table. */
90
90
  readonly require: L.Expression;
91
- /** Luau reaching the bundle's line map — what `console:error` reads to
92
- * name the file a frame was written in. */
91
+ /** Luau reaching the bundle's line map — what a library's runtime reads,
92
+ * as `__LINES__`, to name the file a frame was written in. */
93
93
  readonly lines?: string;
94
+ /** Luau reaching the bundle's error reporter — what a library's runtime
95
+ * reads, as `__FAIL__`, to report an error from a callback the way an
96
+ * error that reached the entry is reported. */
97
+ readonly fail?: string;
94
98
  }
95
99
  interface LowerOptions {
96
100
  /** Lower the file as a module of a bundle. Without it, `import` and
@@ -107,9 +111,12 @@ interface LowerOptions {
107
111
  * `loadLowerings`). The compiler lowers the language; a call written
108
112
  * against a library's types is the library's to explain. */
109
113
  readonly lowerings?: readonly LoadedLowering[];
110
- /** The file's own text, so `console:log` can show a function as the code it
111
- * was written as. Without it a function shows only its type. */
114
+ /** The file's own text, so a library can be told the code an argument was
115
+ * written as (`console:log` shows a function that way). */
112
116
  readonly source?: string;
117
+ /** The file as a library's runtime names it — `src/shop/buy.tilua` —
118
+ * in the call sites it is handed. */
119
+ readonly file?: string;
113
120
  /** Which Lua the output has to run on. Default `"luau"`. `"lua51"` also
114
121
  * lowers what Luau adds to Lua and 5.1 has no syntax for — `a += b`,
115
122
  * `continue`, `if c then a else b` as an expression, `//`. */
package/dist/index.d.ts CHANGED
@@ -88,9 +88,13 @@ interface ModuleContext {
88
88
  /** The bundle's `require`: an expression that, called with a module's
89
89
  * name, returns its exports table. */
90
90
  readonly require: L.Expression;
91
- /** Luau reaching the bundle's line map — what `console:error` reads to
92
- * name the file a frame was written in. */
91
+ /** Luau reaching the bundle's line map — what a library's runtime reads,
92
+ * as `__LINES__`, to name the file a frame was written in. */
93
93
  readonly lines?: string;
94
+ /** Luau reaching the bundle's error reporter — what a library's runtime
95
+ * reads, as `__FAIL__`, to report an error from a callback the way an
96
+ * error that reached the entry is reported. */
97
+ readonly fail?: string;
94
98
  }
95
99
  interface LowerOptions {
96
100
  /** Lower the file as a module of a bundle. Without it, `import` and
@@ -107,9 +111,12 @@ interface LowerOptions {
107
111
  * `loadLowerings`). The compiler lowers the language; a call written
108
112
  * against a library's types is the library's to explain. */
109
113
  readonly lowerings?: readonly LoadedLowering[];
110
- /** The file's own text, so `console:log` can show a function as the code it
111
- * was written as. Without it a function shows only its type. */
114
+ /** The file's own text, so a library can be told the code an argument was
115
+ * written as (`console:log` shows a function that way). */
112
116
  readonly source?: string;
117
+ /** The file as a library's runtime names it — `src/shop/buy.tilua` —
118
+ * in the call sites it is handed. */
119
+ readonly file?: string;
113
120
  /** Which Lua the output has to run on. Default `"luau"`. `"lua51"` also
114
121
  * lowers what Luau adds to Lua and 5.1 has no syntax for — `a += b`,
115
122
  * `continue`, `if c then a else b` as an expression, `//`. */
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  lower,
5
5
  program,
6
6
  resolveConfig
7
- } from "./chunk-QSRDDIU4.js";
7
+ } from "./chunk-2IPPO4V3.js";
8
8
 
9
9
  // src/compile.ts
10
10
  import { readFileSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tilua/compiler",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Compiles tilua to Luau: lowers the @tilua/parser AST to a luau-parser AST and prints it",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -37,7 +37,7 @@
37
37
  "type": "module",
38
38
  "dependencies": {
39
39
  "luau-parser": "^1.0.3",
40
- "@tilua/parser": "^1.0.1"
40
+ "@tilua/parser": "^1.1.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^22.20.2",