@tilua/compiler 3.0.0 → 3.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.
@@ -14,6 +14,7 @@ import { formatType } from "@tilua/parser";
14
14
  import { parse as parseLuau, parseExpressionFromSource } from "luau-parser";
15
15
 
16
16
  // src/luau.ts
17
+ import { LUAU_KEYWORDS, isLuauName } from "@tilua/parser";
17
18
  var NO_SPAN = { line: { start: 0, end: 0 }, column: { start: 0, end: 0 } };
18
19
  var span = () => ({ line: { ...NO_SPAN.line }, column: { ...NO_SPAN.column } });
19
20
  var identifier = (name) => ({ type: "Identifier", name, ...span() });
@@ -74,32 +75,6 @@ var functionBody = (params, statements, hasVarargs = false) => ({
74
75
  });
75
76
  var localFunction = (name, func) => ({ type: "LocalFunctionStatement", name: identifier(name), func, ...span() });
76
77
  var program = (statements) => ({ type: "Program", body: block(statements), ...span() });
77
- var LUAU_KEYWORDS = /* @__PURE__ */ new Set([
78
- "and",
79
- "break",
80
- "do",
81
- "else",
82
- "elseif",
83
- "end",
84
- "false",
85
- "for",
86
- "function",
87
- "if",
88
- "in",
89
- "local",
90
- "nil",
91
- "not",
92
- "or",
93
- "repeat",
94
- "return",
95
- "then",
96
- "true",
97
- "until",
98
- "while"
99
- ]);
100
- function isLuauName(name) {
101
- return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && !LUAU_KEYWORDS.has(name);
102
- }
103
78
 
104
79
  // src/names.ts
105
80
  var Names = class _Names {
@@ -142,6 +117,7 @@ var Names = class _Names {
142
117
 
143
118
  // src/language.ts
144
119
  import { readFileSync } from "fs";
120
+ import { luauString } from "@tilua/parser";
145
121
  var read = (name) => readFileSync(new URL(`../runtime/${name}`, import.meta.url), "utf8");
146
122
  var implemented = (source) => new Set([...source.matchAll(/function __NAME__\.(\w+)/g)].map((m) => m[1]));
147
123
  var array = read("array.luau");
@@ -175,7 +151,7 @@ function objectKeys(type) {
175
151
  orders.push(keys);
176
152
  }
177
153
  const order = JSON.stringify(orders[0] ?? []);
178
- const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) => JSON.stringify(key)).join(", ")} }` : "nil";
154
+ const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) => luauString(key)).join(", ")} }` : "nil";
179
155
  return { known, open: String(open) };
180
156
  }
181
157
  var plugin = {
@@ -1439,6 +1415,9 @@ end
1439
1415
  callValues(node) {
1440
1416
  const chain = optionalChain(node);
1441
1417
  if (chain) return this.optionalChainExpression(chain);
1418
+ if (node.type === "CallExpression" && (node.callee.type === "SuperExpression" || node.callee.type === "MemberExpression" && node.callee.object.type === "SuperExpression")) {
1419
+ return this.expression(node);
1420
+ }
1442
1421
  const object2 = linkObject(node);
1443
1422
  if (node.type === "CallExpression" && object2.type === "Identifier") {
1444
1423
  return this.link(node, this.reference(object2));
@@ -2021,11 +2000,13 @@ import {
2021
2000
  applyDirectives,
2022
2001
  UNUSED_EXPECT_ERROR,
2023
2002
  ParseError,
2024
- LexError
2003
+ LexError,
2004
+ luauString as luauString3
2025
2005
  } from "@tilua/parser";
2026
2006
  import { parse as parseLuau2, parseExpressionFromSource as parseExpressionFromSource2, print } from "luau-parser";
2027
2007
 
2028
2008
  // src/linemap.ts
2009
+ import { luauString as luauString2 } from "@tilua/parser";
2029
2010
  function statementsOf(node) {
2030
2011
  const out = [];
2031
2012
  const visit = (value) => {
@@ -2058,7 +2039,7 @@ function buildLineMap(emitted, printed, origins) {
2058
2039
  }
2059
2040
  function lineMapSource(map) {
2060
2041
  const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
2061
- const quoted = (s) => JSON.stringify(s.replace(/\\/g, "/"));
2042
+ const quoted = (s) => luauString2(s.replace(/\\/g, "/"));
2062
2043
  return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
2063
2044
  }
2064
2045
 
@@ -2185,7 +2166,7 @@ async function bundle(options) {
2185
2166
  const result = names.fresh("result");
2186
2167
  const entryStatements = parseLuau2(`
2187
2168
  local ${ok}, ${result} = xpcall(function()
2188
- return ${G}.require(${JSON.stringify(sources.get(entry).name)})
2169
+ return ${G}.require(${luauString3(sources.get(entry).name)})
2189
2170
  end, ${G}.fail)
2190
2171
  if not ${ok} then
2191
2172
  error(${result}, 0)
@@ -2214,19 +2195,64 @@ ${G} = {
2214
2195
  -- and a traceback of those places follows. A message that is not a string
2215
2196
  -- is left as it is, and a position that is not this bundle's (a library
2216
2197
  -- that raised its error with one already mapped) is left alone.
2198
+ --
2199
+ -- It is an \`xpcall\` handler, and a handler that fails loses the error
2200
+ -- altogether: Luau reports only "error in error handling". So the work is
2201
+ -- done under \`pcall\`, leaning on nothing it has not checked is there (an
2202
+ -- executor may lack \`debug.info\`, or refuse it), and an error it cannot
2203
+ -- map is still answered as Luau raised it, with why.
2217
2204
  fail = function(message)
2205
+ -- Taken first, before anything that can fail. Level 3 is the frame
2206
+ -- that raised: past \`pcall\` and this handler.
2207
+ local trace
2208
+ if debug ~= nil and debug.traceback ~= nil then
2209
+ local ok, text = pcall(debug.traceback, "", 3)
2210
+ if ok and type(text) == "string" then
2211
+ trace = text
2212
+ end
2213
+ end
2214
+ local ok, described = pcall(${G}.describe, message, trace)
2215
+ if ok then
2216
+ return described
2217
+ end
2218
+ if type(message) ~= "string" then
2219
+ return message
2220
+ end
2221
+ local why = type(described) == "string" and described or "unknown"
2222
+ return message .. "\\n(tilua could not map this error to the project's files: " .. why .. ")" .. (trace or "")
2223
+ end,
2224
+ -- \`fail\`'s work. \`trace\` is a traceback from where the error was
2225
+ -- raised, or nil when none could be taken.
2226
+ describe = function(message, trace)
2218
2227
  if type(message) ~= "string" then
2219
2228
  return message
2220
2229
  end
2221
2230
  local lines = ${G}.lines
2222
2231
  if lines == nil then
2223
- return debug.traceback(message, 2)
2232
+ return message .. (trace or "")
2224
2233
  end
2234
+ -- The bundle's name, as a position in a message spells it: where this
2235
+ -- function is, asked of the debug library by the function rather
2236
+ -- than by a level, which \`pcall\` would shift. Failing that, a message
2237
+ -- starts with the position it was raised at.
2225
2238
  local chunk
2226
- if debug.info ~= nil then
2227
- chunk = debug.info(1, "s")
2228
- else
2229
- chunk = debug.getinfo(1, "S").short_src
2239
+ if debug ~= nil and debug.info ~= nil then
2240
+ local found, source = pcall(debug.info, ${G}.describe, "s")
2241
+ if found and type(source) == "string" then
2242
+ chunk = source
2243
+ end
2244
+ end
2245
+ if chunk == nil and debug ~= nil and debug.getinfo ~= nil then
2246
+ local found, info = pcall(debug.getinfo, ${G}.describe, "S")
2247
+ if found and type(info) == "table" and type(info.short_src) == "string" then
2248
+ chunk = info.short_src
2249
+ end
2250
+ end
2251
+ if chunk == nil then
2252
+ chunk = message:match("^(.-):%d+:")
2253
+ end
2254
+ if chunk == nil then
2255
+ return message .. (trace or "")
2230
2256
  end
2231
2257
  local prefix = chunk:gsub("%p", "%%%0")
2232
2258
  local function place(line)
@@ -2240,7 +2266,7 @@ ${G} = {
2240
2266
  return place(line)
2241
2267
  end)
2242
2268
  local frames = {}
2243
- for frame in debug.traceback("", 2):gmatch("[^\\n]+") do
2269
+ for frame in (trace or ""):gmatch("[^\\n]+") do
2244
2270
  local line = frame:match(prefix .. ":(%d+)")
2245
2271
  local at = line ~= nil and place(line) or nil
2246
2272
  if at ~= nil then
package/dist/cli.cjs CHANGED
@@ -12,7 +12,7 @@ var import_node_path3 = require("path");
12
12
  // src/bundle.ts
13
13
  var import_node_fs2 = require("fs");
14
14
  var import_node_path2 = require("path");
15
- var import_parser3 = require("@tilua/parser");
15
+ var import_parser6 = require("@tilua/parser");
16
16
  var import_luau_parser2 = require("luau-parser");
17
17
 
18
18
  // src/config.ts
@@ -27,10 +27,11 @@ function resolveConfig(input, entry2, cwd = process.cwd()) {
27
27
  }
28
28
 
29
29
  // src/lower.ts
30
- var import_parser2 = require("@tilua/parser");
30
+ var import_parser4 = require("@tilua/parser");
31
31
  var import_luau_parser = require("luau-parser");
32
32
 
33
33
  // src/luau.ts
34
+ var import_parser2 = require("@tilua/parser");
34
35
  var NO_SPAN = { line: { start: 0, end: 0 }, column: { start: 0, end: 0 } };
35
36
  var span = () => ({ line: { ...NO_SPAN.line }, column: { ...NO_SPAN.column } });
36
37
  var identifier = (name) => ({ type: "Identifier", name, ...span() });
@@ -38,7 +39,7 @@ var nil = () => ({ type: "NilLiteral", ...span() });
38
39
  var boolean = (value) => ({ type: "BooleanLiteral", value, ...span() });
39
40
  var number = (value, raw = String(value)) => ({ type: "NumberLiteral", value, raw, ...span() });
40
41
  var string = (value) => ({ type: "StringLiteral", value, raw: "", ...span() });
41
- var member = (object2, property) => isLuauName(property) ? { type: "MemberExpression", object: object2, property: identifier(property), ...span() } : index(object2, string(property));
42
+ var member = (object2, property) => (0, import_parser2.isLuauName)(property) ? { type: "MemberExpression", object: object2, property: identifier(property), ...span() } : index(object2, string(property));
42
43
  var index = (object2, key) => ({ type: "IndexExpression", object: object2, index: key, ...span() });
43
44
  var call = (callee, args2) => ({ type: "CallExpression", callee, arguments: args2, ...span() });
44
45
  var methodCall = (object2, method, args2) => ({ type: "MethodCallExpression", object: object2, method: identifier(method), arguments: args2, ...span() });
@@ -47,7 +48,7 @@ var binary = (operator, left, right) => ({ type: "BinaryExpression", operator, l
47
48
  var unary = (operator, argument) => ({ type: "UnaryExpression", operator, argument, ...span() });
48
49
  var parenthesized = (expression) => ({ type: "ParenthesizedExpression", expression, ...span() });
49
50
  var table = (fields) => ({ type: "TableExpression", fields, ...span() });
50
- var field = (key, value) => isLuauName(key) ? { type: "TableFieldNamed", name: identifier(key), value } : { type: "TableFieldComputed", key: string(key), value };
51
+ var field = (key, value) => (0, import_parser2.isLuauName)(key) ? { type: "TableFieldNamed", name: identifier(key), value } : { type: "TableFieldComputed", key: string(key), value };
51
52
  var block = (statements) => ({ type: "Block", statements, ...span() });
52
53
  var local = (names, init) => ({
53
54
  type: "LocalStatement",
@@ -90,32 +91,6 @@ var functionBody = (params, statements, hasVarargs = false) => ({
90
91
  ...span()
91
92
  });
92
93
  var localFunction = (name, func) => ({ type: "LocalFunctionStatement", name: identifier(name), func, ...span() });
93
- var LUAU_KEYWORDS = /* @__PURE__ */ new Set([
94
- "and",
95
- "break",
96
- "do",
97
- "else",
98
- "elseif",
99
- "end",
100
- "false",
101
- "for",
102
- "function",
103
- "if",
104
- "in",
105
- "local",
106
- "nil",
107
- "not",
108
- "or",
109
- "repeat",
110
- "return",
111
- "then",
112
- "true",
113
- "until",
114
- "while"
115
- ]);
116
- function isLuauName(name) {
117
- return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && !LUAU_KEYWORDS.has(name);
118
- }
119
94
 
120
95
  // src/names.ts
121
96
  var Names = class _Names {
@@ -158,6 +133,7 @@ var Names = class _Names {
158
133
 
159
134
  // src/language.ts
160
135
  var import_node_fs = require("fs");
136
+ var import_parser3 = require("@tilua/parser");
161
137
  var read = (name) => (0, import_node_fs.readFileSync)(new URL(`../runtime/${name}`, importMetaUrl), "utf8");
162
138
  var implemented = (source) => new Set([...source.matchAll(/function __NAME__\.(\w+)/g)].map((m) => m[1]));
163
139
  var array = read("array.luau");
@@ -191,7 +167,7 @@ function objectKeys(type) {
191
167
  orders.push(keys);
192
168
  }
193
169
  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";
170
+ const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) => (0, import_parser3.luauString)(key)).join(", ")} }` : "nil";
195
171
  return { known, open: String(open) };
196
172
  }
197
173
  var plugin = {
@@ -272,7 +248,7 @@ var Lowerer = class {
272
248
  // --------------------------------------------------------
273
249
  /** A tilua name as Luau can write it: `local` is a keyword there. */
274
250
  name(name) {
275
- if (!LUAU_KEYWORDS.has(name)) return name;
251
+ if (!import_parser2.LUAU_KEYWORDS.has(name)) return name;
276
252
  let renamed = this.renamed.get(name);
277
253
  if (!renamed) {
278
254
  renamed = this.names.fresh(`${name}_`);
@@ -376,7 +352,7 @@ var Lowerer = class {
376
352
  const type = this.options.types?.typeOf.get(argument);
377
353
  return {
378
354
  type,
379
- typeText: type ? (0, import_parser2.formatType)(type) : void 0,
355
+ typeText: type ? (0, import_parser4.formatType)(type) : void 0,
380
356
  source: this.writtenSource(argument),
381
357
  declaration: declaration ? this.writtenSource(declaration) : void 0,
382
358
  spread: argument.type === "SpreadElement"
@@ -1455,6 +1431,9 @@ end
1455
1431
  callValues(node) {
1456
1432
  const chain = optionalChain(node);
1457
1433
  if (chain) return this.optionalChainExpression(chain);
1434
+ if (node.type === "CallExpression" && (node.callee.type === "SuperExpression" || node.callee.type === "MemberExpression" && node.callee.object.type === "SuperExpression")) {
1435
+ return this.expression(node);
1436
+ }
1458
1437
  const object2 = linkObject(node);
1459
1438
  if (node.type === "CallExpression" && object2.type === "Identifier") {
1460
1439
  return this.link(node, this.reference(object2));
@@ -1520,7 +1499,7 @@ end
1520
1499
  ...args2
1521
1500
  ]);
1522
1501
  }
1523
- if (!isLuauName(node.method.name)) {
1502
+ if (!import_parser2.isLuauName(node.method.name)) {
1524
1503
  this.report(node.method, `'${node.method.name}' is a Luau keyword and cannot be called with ':'`);
1525
1504
  }
1526
1505
  return methodCall(object2, node.method.name, args2);
@@ -1882,7 +1861,7 @@ function topLevelDeclaration(statements, name) {
1882
1861
  function moduleName(key) {
1883
1862
  const last = key.split("/").filter(Boolean).pop() ?? "module";
1884
1863
  const cleaned = last.replace(/[^A-Za-z0-9_]/g, "_");
1885
- return /^[A-Za-z_]/.test(cleaned) && !LUAU_KEYWORDS.has(cleaned) ? cleaned : `module_${cleaned}`;
1864
+ return /^[A-Za-z_]/.test(cleaned) && !import_parser2.LUAU_KEYWORDS.has(cleaned) ? cleaned : `module_${cleaned}`;
1886
1865
  }
1887
1866
  function alwaysTruthy(value) {
1888
1867
  switch (value.type) {
@@ -1997,6 +1976,7 @@ var CLASS_METAMETHODS = [
1997
1976
  ];
1998
1977
 
1999
1978
  // src/linemap.ts
1979
+ var import_parser5 = require("@tilua/parser");
2000
1980
  function statementsOf(node) {
2001
1981
  const out2 = [];
2002
1982
  const visit = (value) => {
@@ -2029,7 +2009,7 @@ function buildLineMap(emitted, printed, origins) {
2029
2009
  }
2030
2010
  function lineMapSource(map) {
2031
2011
  const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
2032
- const quoted = (s) => JSON.stringify(s.replace(/\\/g, "/"));
2012
+ const quoted = (s) => (0, import_parser5.luauString)(s.replace(/\\/g, "/"));
2033
2013
  return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
2034
2014
  }
2035
2015
 
@@ -2078,13 +2058,13 @@ async function bundle(options) {
2078
2058
  const parsed = parseFile(file, diagnostics, directives);
2079
2059
  if (!parsed) return { modules: [], diagnostics };
2080
2060
  const { program: program2, text } = parsed;
2081
- const scopes = (0, import_parser3.analyzeScopes)(program2);
2061
+ const scopes = (0, import_parser6.analyzeScopes)(program2);
2082
2062
  for (const d of scopes.diagnostics) {
2083
2063
  diagnostics.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "scope" });
2084
2064
  }
2085
2065
  sources.set(file, { file, name: nameOf(file), text, program: program2, scopes });
2086
2066
  for (const specifier of importedSpecifiers(program2)) {
2087
- const target = (0, import_parser3.resolveModulePath)(file, specifier, config2);
2067
+ const target = (0, import_parser6.resolveModulePath)(file, specifier, config2);
2088
2068
  if (target && !target.endsWith(".d.tilua")) queue.push(target);
2089
2069
  }
2090
2070
  }
@@ -2118,7 +2098,7 @@ async function bundle(options) {
2118
2098
  lines: `${G}.lines`,
2119
2099
  fail: `${G}.fail`,
2120
2100
  resolve: (specifier) => {
2121
- const target = (0, import_parser3.resolveModulePath)(file, specifier, config2);
2101
+ const target = (0, import_parser6.resolveModulePath)(file, specifier, config2);
2122
2102
  if (!target || target.endsWith(".d.tilua") || !sources.has(target)) return void 0;
2123
2103
  pending.push(target);
2124
2104
  return sources.get(target).name;
@@ -2140,14 +2120,14 @@ async function bundle(options) {
2140
2120
  }
2141
2121
  for (const [file, fileDirectives] of directives) {
2142
2122
  const semantic = diagnostics.filter((d) => d.file === file && (d.category === "scope" || d.category === "type"));
2143
- const { kept, unusedExpectErrors } = (0, import_parser3.applyDirectives)(fileDirectives, semantic, (d) => d.line);
2123
+ const { kept, unusedExpectErrors } = (0, import_parser6.applyDirectives)(fileDirectives, semantic, (d) => d.line);
2144
2124
  const suppressed = new Set(semantic.filter((d) => !kept.includes(d)));
2145
2125
  const remaining = diagnostics.filter((d) => !suppressed.has(d));
2146
2126
  diagnostics.length = 0;
2147
2127
  diagnostics.push(...remaining);
2148
2128
  if (options.typeCheck === false) continue;
2149
2129
  for (const d of unusedExpectErrors) {
2150
- diagnostics.push({ file, message: import_parser3.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
2130
+ diagnostics.push({ file, message: import_parser6.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
2151
2131
  }
2152
2132
  }
2153
2133
  const moduleProblems = new Set(diagnostics.filter((d) => d.category === "module").map((d) => `${d.file}:${d.line}:${d.column}:${d.message}`));
@@ -2183,7 +2163,7 @@ async function bundle(options) {
2183
2163
  const result = names.fresh("result");
2184
2164
  const entryStatements = (0, import_luau_parser2.parse)(`
2185
2165
  local ${ok}, ${result} = xpcall(function()
2186
- return ${G}.require(${JSON.stringify(sources.get(entry2).name)})
2166
+ return ${G}.require(${(0, import_parser6.luauString)(sources.get(entry2).name)})
2187
2167
  end, ${G}.fail)
2188
2168
  if not ${ok} then
2189
2169
  error(${result}, 0)
@@ -2212,19 +2192,64 @@ ${G} = {
2212
2192
  -- and a traceback of those places follows. A message that is not a string
2213
2193
  -- is left as it is, and a position that is not this bundle's (a library
2214
2194
  -- that raised its error with one already mapped) is left alone.
2195
+ --
2196
+ -- It is an \`xpcall\` handler, and a handler that fails loses the error
2197
+ -- altogether: Luau reports only "error in error handling". So the work is
2198
+ -- done under \`pcall\`, leaning on nothing it has not checked is there (an
2199
+ -- executor may lack \`debug.info\`, or refuse it), and an error it cannot
2200
+ -- map is still answered as Luau raised it, with why.
2215
2201
  fail = function(message)
2202
+ -- Taken first, before anything that can fail. Level 3 is the frame
2203
+ -- that raised: past \`pcall\` and this handler.
2204
+ local trace
2205
+ if debug ~= nil and debug.traceback ~= nil then
2206
+ local ok, text = pcall(debug.traceback, "", 3)
2207
+ if ok and type(text) == "string" then
2208
+ trace = text
2209
+ end
2210
+ end
2211
+ local ok, described = pcall(${G}.describe, message, trace)
2212
+ if ok then
2213
+ return described
2214
+ end
2215
+ if type(message) ~= "string" then
2216
+ return message
2217
+ end
2218
+ local why = type(described) == "string" and described or "unknown"
2219
+ return message .. "\\n(tilua could not map this error to the project's files: " .. why .. ")" .. (trace or "")
2220
+ end,
2221
+ -- \`fail\`'s work. \`trace\` is a traceback from where the error was
2222
+ -- raised, or nil when none could be taken.
2223
+ describe = function(message, trace)
2216
2224
  if type(message) ~= "string" then
2217
2225
  return message
2218
2226
  end
2219
2227
  local lines = ${G}.lines
2220
2228
  if lines == nil then
2221
- return debug.traceback(message, 2)
2229
+ return message .. (trace or "")
2222
2230
  end
2231
+ -- The bundle's name, as a position in a message spells it: where this
2232
+ -- function is, asked of the debug library by the function rather
2233
+ -- than by a level, which \`pcall\` would shift. Failing that, a message
2234
+ -- starts with the position it was raised at.
2223
2235
  local chunk
2224
- if debug.info ~= nil then
2225
- chunk = debug.info(1, "s")
2226
- else
2227
- chunk = debug.getinfo(1, "S").short_src
2236
+ if debug ~= nil and debug.info ~= nil then
2237
+ local found, source = pcall(debug.info, ${G}.describe, "s")
2238
+ if found and type(source) == "string" then
2239
+ chunk = source
2240
+ end
2241
+ end
2242
+ if chunk == nil and debug ~= nil and debug.getinfo ~= nil then
2243
+ local found, info = pcall(debug.getinfo, ${G}.describe, "S")
2244
+ if found and type(info) == "table" and type(info.short_src) == "string" then
2245
+ chunk = info.short_src
2246
+ end
2247
+ end
2248
+ if chunk == nil then
2249
+ chunk = message:match("^(.-):%d+:")
2250
+ end
2251
+ if chunk == nil then
2252
+ return message .. (trace or "")
2228
2253
  end
2229
2254
  local prefix = chunk:gsub("%p", "%%%0")
2230
2255
  local function place(line)
@@ -2238,7 +2263,7 @@ ${G} = {
2238
2263
  return place(line)
2239
2264
  end)
2240
2265
  local frames = {}
2241
- for frame in debug.traceback("", 2):gmatch("[^\\n]+") do
2266
+ for frame in (trace or ""):gmatch("[^\\n]+") do
2242
2267
  local line = frame:match(prefix .. ":(%d+)")
2243
2268
  local at = line ~= nil and place(line) or nil
2244
2269
  if at ~= nil then
@@ -2323,13 +2348,13 @@ function parseFile(file, diagnostics, directives) {
2323
2348
  return void 0;
2324
2349
  }
2325
2350
  try {
2326
- const program = (0, import_parser3.parse)(text);
2327
- const found = (0, import_parser3.directivesOf)(text);
2351
+ const program = (0, import_parser6.parse)(text);
2352
+ const found = (0, import_parser6.directivesOf)(text);
2328
2353
  directives.set(file, found);
2329
2354
  if (stamp !== void 0) parsedFiles.set(file, { stamp, program, text, directives: found });
2330
2355
  return { program, text };
2331
2356
  } catch (error) {
2332
- if (error instanceof import_parser3.ParseError || error instanceof import_parser3.LexError) {
2357
+ if (error instanceof import_parser6.ParseError || error instanceof import_parser6.LexError) {
2333
2358
  const { line, column } = error;
2334
2359
  diagnostics.push({ file, message: error.message, line, column, category: "syntax" });
2335
2360
  return void 0;
@@ -2351,7 +2376,7 @@ function parsedLibrary(file) {
2351
2376
  }
2352
2377
  const cached = libraryCache.get(file);
2353
2378
  if (cached && cached.stamp === stamp) return cached.program;
2354
- const program = (0, import_parser3.parse)((0, import_node_fs2.readFileSync)(file, "utf8"));
2379
+ const program = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(file, "utf8"));
2355
2380
  libraryCache.set(file, { stamp, program });
2356
2381
  return program;
2357
2382
  }
@@ -2359,7 +2384,7 @@ var moduleAnalyses = /* @__PURE__ */ new Map();
2359
2384
  function analyzeModules(modules, config2) {
2360
2385
  const out2 = [];
2361
2386
  const analyses = /* @__PURE__ */ new Map();
2362
- const libraries = config2 ? (0, import_parser3.resolveTypeLibraries)(config2) : { files: [], lowerings: [], problems: [] };
2387
+ const libraries = config2 ? (0, import_parser6.resolveTypeLibraries)(config2) : { files: [], lowerings: [], problems: [] };
2363
2388
  for (const p of libraries.problems) out2.push({ file: p.file, message: p.message, line: p.line ?? 1, column: p.column ?? 1, category: "config" });
2364
2389
  const libs = libraries.files.map(parsedLibrary);
2365
2390
  const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
@@ -2379,14 +2404,14 @@ function analyzeModules(modules, config2) {
2379
2404
  readWhile.set(file, remembered.read);
2380
2405
  return remembered.result;
2381
2406
  }
2382
- const scopes = (0, import_parser3.analyzeScopes)(program, { builtinGlobals: globals, reportUndeclared: libs.length > 0 });
2407
+ const scopes = (0, import_parser6.analyzeScopes)(program, { builtinGlobals: globals, reportUndeclared: libs.length > 0 });
2383
2408
  const said = [];
2384
2409
  for (const d of scopes.diagnostics) {
2385
2410
  if (d.kind !== "undeclared") continue;
2386
2411
  said.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "type" });
2387
2412
  }
2388
2413
  const before = partials;
2389
- const types = (0, import_parser3.analyzeTypes)(program, scopes, {
2414
+ const types = (0, import_parser6.analyzeTypes)(program, scopes, {
2390
2415
  libs,
2391
2416
  resolveModule: resolverFor(file),
2392
2417
  reportUnknownTypes: libs.length > 0
@@ -2409,7 +2434,7 @@ function analyzeModules(modules, config2) {
2409
2434
  return result;
2410
2435
  };
2411
2436
  const resolverFor = (file) => (specifier) => {
2412
- const target = (0, import_parser3.resolveModulePath)(file, specifier, config2);
2437
+ const target = (0, import_parser6.resolveModulePath)(file, specifier, config2);
2413
2438
  if (!target) return void 0;
2414
2439
  const noteRead = () => {
2415
2440
  const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
@@ -2430,16 +2455,16 @@ function analyzeModules(modules, config2) {
2430
2455
  let program = byFile.get(target)?.program;
2431
2456
  if (!program) {
2432
2457
  try {
2433
- program = (0, import_parser3.parse)((0, import_node_fs2.readFileSync)(target, "utf8"));
2458
+ program = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(target, "utf8"));
2434
2459
  } catch {
2435
2460
  return void 0;
2436
2461
  }
2437
2462
  }
2438
2463
  inProgress.add(target);
2439
2464
  try {
2440
- const scopes = (0, import_parser3.analyzeScopes)(program, { builtinGlobals: globals });
2441
- const types = byFile.has(target) ? analyzeModule(target, program).types : (0, import_parser3.analyzeTypes)(program, scopes, { libs, resolveModule: resolverFor(target), diagnostics: false });
2442
- const exports2 = (0, import_parser3.moduleExports)(program, scopes, types, resolverFor(target));
2465
+ const scopes = (0, import_parser6.analyzeScopes)(program, { builtinGlobals: globals });
2466
+ const types = byFile.has(target) ? analyzeModule(target, program).types : (0, import_parser6.analyzeTypes)(program, scopes, { libs, resolveModule: resolverFor(target), diagnostics: false });
2467
+ const exports2 = (0, import_parser6.moduleExports)(program, scopes, types, resolverFor(target));
2443
2468
  exportsCache.set(target, exports2);
2444
2469
  noteRead();
2445
2470
  return exports2;
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  bundle
4
- } from "./chunk-X4DM2BX2.js";
4
+ } from "./chunk-VANZBLCL.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { writeFileSync } from "fs";
package/dist/index.cjs CHANGED
@@ -35,7 +35,7 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
35
35
  // src/bundle.ts
36
36
  var import_node_fs2 = require("fs");
37
37
  var import_node_path2 = require("path");
38
- var import_parser3 = require("@tilua/parser");
38
+ var import_parser6 = require("@tilua/parser");
39
39
  var import_luau_parser2 = require("luau-parser");
40
40
 
41
41
  // src/config.ts
@@ -50,10 +50,11 @@ function resolveConfig(input, entry, cwd = process.cwd()) {
50
50
  }
51
51
 
52
52
  // src/lower.ts
53
- var import_parser2 = require("@tilua/parser");
53
+ var import_parser4 = require("@tilua/parser");
54
54
  var import_luau_parser = require("luau-parser");
55
55
 
56
56
  // src/luau.ts
57
+ var import_parser2 = require("@tilua/parser");
57
58
  var NO_SPAN = { line: { start: 0, end: 0 }, column: { start: 0, end: 0 } };
58
59
  var span = () => ({ line: { ...NO_SPAN.line }, column: { ...NO_SPAN.column } });
59
60
  var identifier = (name) => ({ type: "Identifier", name, ...span() });
@@ -61,7 +62,7 @@ var nil = () => ({ type: "NilLiteral", ...span() });
61
62
  var boolean = (value) => ({ type: "BooleanLiteral", value, ...span() });
62
63
  var number = (value, raw = String(value)) => ({ type: "NumberLiteral", value, raw, ...span() });
63
64
  var string = (value) => ({ type: "StringLiteral", value, raw: "", ...span() });
64
- var member = (object2, property) => isLuauName(property) ? { type: "MemberExpression", object: object2, property: identifier(property), ...span() } : index(object2, string(property));
65
+ var member = (object2, property) => (0, import_parser2.isLuauName)(property) ? { type: "MemberExpression", object: object2, property: identifier(property), ...span() } : index(object2, string(property));
65
66
  var index = (object2, key) => ({ type: "IndexExpression", object: object2, index: key, ...span() });
66
67
  var call = (callee, args) => ({ type: "CallExpression", callee, arguments: args, ...span() });
67
68
  var methodCall = (object2, method, args) => ({ type: "MethodCallExpression", object: object2, method: identifier(method), arguments: args, ...span() });
@@ -70,7 +71,7 @@ var binary = (operator, left, right) => ({ type: "BinaryExpression", operator, l
70
71
  var unary = (operator, argument) => ({ type: "UnaryExpression", operator, argument, ...span() });
71
72
  var parenthesized = (expression) => ({ type: "ParenthesizedExpression", expression, ...span() });
72
73
  var table = (fields) => ({ type: "TableExpression", fields, ...span() });
73
- var field = (key, value) => isLuauName(key) ? { type: "TableFieldNamed", name: identifier(key), value } : { type: "TableFieldComputed", key: string(key), value };
74
+ var field = (key, value) => (0, import_parser2.isLuauName)(key) ? { type: "TableFieldNamed", name: identifier(key), value } : { type: "TableFieldComputed", key: string(key), value };
74
75
  var block = (statements) => ({ type: "Block", statements, ...span() });
75
76
  var local = (names, init) => ({
76
77
  type: "LocalStatement",
@@ -114,32 +115,6 @@ var functionBody = (params, statements, hasVarargs = false) => ({
114
115
  });
115
116
  var localFunction = (name, func) => ({ type: "LocalFunctionStatement", name: identifier(name), func, ...span() });
116
117
  var program = (statements) => ({ type: "Program", body: block(statements), ...span() });
117
- var LUAU_KEYWORDS = /* @__PURE__ */ new Set([
118
- "and",
119
- "break",
120
- "do",
121
- "else",
122
- "elseif",
123
- "end",
124
- "false",
125
- "for",
126
- "function",
127
- "if",
128
- "in",
129
- "local",
130
- "nil",
131
- "not",
132
- "or",
133
- "repeat",
134
- "return",
135
- "then",
136
- "true",
137
- "until",
138
- "while"
139
- ]);
140
- function isLuauName(name) {
141
- return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && !LUAU_KEYWORDS.has(name);
142
- }
143
118
 
144
119
  // src/names.ts
145
120
  var Names = class _Names {
@@ -182,6 +157,7 @@ var Names = class _Names {
182
157
 
183
158
  // src/language.ts
184
159
  var import_node_fs = require("fs");
160
+ var import_parser3 = require("@tilua/parser");
185
161
  var read = (name) => (0, import_node_fs.readFileSync)(new URL(`../runtime/${name}`, importMetaUrl), "utf8");
186
162
  var implemented = (source) => new Set([...source.matchAll(/function __NAME__\.(\w+)/g)].map((m) => m[1]));
187
163
  var array = read("array.luau");
@@ -215,7 +191,7 @@ function objectKeys(type) {
215
191
  orders.push(keys);
216
192
  }
217
193
  const order = JSON.stringify(orders[0] ?? []);
218
- const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) => JSON.stringify(key)).join(", ")} }` : "nil";
194
+ const known = orders.length && orders.every((keys) => JSON.stringify(keys) === order) ? `{ ${orders[0].map((key) => (0, import_parser3.luauString)(key)).join(", ")} }` : "nil";
219
195
  return { known, open: String(open) };
220
196
  }
221
197
  var plugin = {
@@ -296,7 +272,7 @@ var Lowerer = class {
296
272
  // --------------------------------------------------------
297
273
  /** A tilua name as Luau can write it: `local` is a keyword there. */
298
274
  name(name) {
299
- if (!LUAU_KEYWORDS.has(name)) return name;
275
+ if (!import_parser2.LUAU_KEYWORDS.has(name)) return name;
300
276
  let renamed = this.renamed.get(name);
301
277
  if (!renamed) {
302
278
  renamed = this.names.fresh(`${name}_`);
@@ -400,7 +376,7 @@ var Lowerer = class {
400
376
  const type = this.options.types?.typeOf.get(argument);
401
377
  return {
402
378
  type,
403
- typeText: type ? (0, import_parser2.formatType)(type) : void 0,
379
+ typeText: type ? (0, import_parser4.formatType)(type) : void 0,
404
380
  source: this.writtenSource(argument),
405
381
  declaration: declaration ? this.writtenSource(declaration) : void 0,
406
382
  spread: argument.type === "SpreadElement"
@@ -1479,6 +1455,9 @@ end
1479
1455
  callValues(node) {
1480
1456
  const chain = optionalChain(node);
1481
1457
  if (chain) return this.optionalChainExpression(chain);
1458
+ if (node.type === "CallExpression" && (node.callee.type === "SuperExpression" || node.callee.type === "MemberExpression" && node.callee.object.type === "SuperExpression")) {
1459
+ return this.expression(node);
1460
+ }
1482
1461
  const object2 = linkObject(node);
1483
1462
  if (node.type === "CallExpression" && object2.type === "Identifier") {
1484
1463
  return this.link(node, this.reference(object2));
@@ -1544,7 +1523,7 @@ end
1544
1523
  ...args
1545
1524
  ]);
1546
1525
  }
1547
- if (!isLuauName(node.method.name)) {
1526
+ if (!import_parser2.isLuauName(node.method.name)) {
1548
1527
  this.report(node.method, `'${node.method.name}' is a Luau keyword and cannot be called with ':'`);
1549
1528
  }
1550
1529
  return methodCall(object2, node.method.name, args);
@@ -1906,7 +1885,7 @@ function topLevelDeclaration(statements, name) {
1906
1885
  function moduleName(key) {
1907
1886
  const last = key.split("/").filter(Boolean).pop() ?? "module";
1908
1887
  const cleaned = last.replace(/[^A-Za-z0-9_]/g, "_");
1909
- return /^[A-Za-z_]/.test(cleaned) && !LUAU_KEYWORDS.has(cleaned) ? cleaned : `module_${cleaned}`;
1888
+ return /^[A-Za-z_]/.test(cleaned) && !import_parser2.LUAU_KEYWORDS.has(cleaned) ? cleaned : `module_${cleaned}`;
1910
1889
  }
1911
1890
  function alwaysTruthy(value) {
1912
1891
  switch (value.type) {
@@ -2021,6 +2000,7 @@ var CLASS_METAMETHODS = [
2021
2000
  ];
2022
2001
 
2023
2002
  // src/linemap.ts
2003
+ var import_parser5 = require("@tilua/parser");
2024
2004
  function statementsOf(node) {
2025
2005
  const out = [];
2026
2006
  const visit = (value) => {
@@ -2053,7 +2033,7 @@ function buildLineMap(emitted, printed, origins) {
2053
2033
  }
2054
2034
  function lineMapSource(map) {
2055
2035
  const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
2056
- const quoted = (s) => JSON.stringify(s.replace(/\\/g, "/"));
2036
+ const quoted = (s) => (0, import_parser5.luauString)(s.replace(/\\/g, "/"));
2057
2037
  return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
2058
2038
  }
2059
2039
 
@@ -2102,13 +2082,13 @@ async function bundle(options) {
2102
2082
  const parsed = parseFile(file, diagnostics, directives);
2103
2083
  if (!parsed) return { modules: [], diagnostics };
2104
2084
  const { program: program3, text } = parsed;
2105
- const scopes = (0, import_parser3.analyzeScopes)(program3);
2085
+ const scopes = (0, import_parser6.analyzeScopes)(program3);
2106
2086
  for (const d of scopes.diagnostics) {
2107
2087
  diagnostics.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "scope" });
2108
2088
  }
2109
2089
  sources.set(file, { file, name: nameOf(file), text, program: program3, scopes });
2110
2090
  for (const specifier of importedSpecifiers(program3)) {
2111
- const target = (0, import_parser3.resolveModulePath)(file, specifier, config);
2091
+ const target = (0, import_parser6.resolveModulePath)(file, specifier, config);
2112
2092
  if (target && !target.endsWith(".d.tilua")) queue.push(target);
2113
2093
  }
2114
2094
  }
@@ -2142,7 +2122,7 @@ async function bundle(options) {
2142
2122
  lines: `${G}.lines`,
2143
2123
  fail: `${G}.fail`,
2144
2124
  resolve: (specifier) => {
2145
- const target = (0, import_parser3.resolveModulePath)(file, specifier, config);
2125
+ const target = (0, import_parser6.resolveModulePath)(file, specifier, config);
2146
2126
  if (!target || target.endsWith(".d.tilua") || !sources.has(target)) return void 0;
2147
2127
  pending.push(target);
2148
2128
  return sources.get(target).name;
@@ -2164,14 +2144,14 @@ async function bundle(options) {
2164
2144
  }
2165
2145
  for (const [file, fileDirectives] of directives) {
2166
2146
  const semantic = diagnostics.filter((d) => d.file === file && (d.category === "scope" || d.category === "type"));
2167
- const { kept, unusedExpectErrors } = (0, import_parser3.applyDirectives)(fileDirectives, semantic, (d) => d.line);
2147
+ const { kept, unusedExpectErrors } = (0, import_parser6.applyDirectives)(fileDirectives, semantic, (d) => d.line);
2168
2148
  const suppressed = new Set(semantic.filter((d) => !kept.includes(d)));
2169
2149
  const remaining = diagnostics.filter((d) => !suppressed.has(d));
2170
2150
  diagnostics.length = 0;
2171
2151
  diagnostics.push(...remaining);
2172
2152
  if (options.typeCheck === false) continue;
2173
2153
  for (const d of unusedExpectErrors) {
2174
- diagnostics.push({ file, message: import_parser3.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
2154
+ diagnostics.push({ file, message: import_parser6.UNUSED_EXPECT_ERROR, line: d.line, column: d.column, category: "type" });
2175
2155
  }
2176
2156
  }
2177
2157
  const moduleProblems = new Set(diagnostics.filter((d) => d.category === "module").map((d) => `${d.file}:${d.line}:${d.column}:${d.message}`));
@@ -2207,7 +2187,7 @@ async function bundle(options) {
2207
2187
  const result = names.fresh("result");
2208
2188
  const entryStatements = (0, import_luau_parser2.parse)(`
2209
2189
  local ${ok}, ${result} = xpcall(function()
2210
- return ${G}.require(${JSON.stringify(sources.get(entry).name)})
2190
+ return ${G}.require(${(0, import_parser6.luauString)(sources.get(entry).name)})
2211
2191
  end, ${G}.fail)
2212
2192
  if not ${ok} then
2213
2193
  error(${result}, 0)
@@ -2236,19 +2216,64 @@ ${G} = {
2236
2216
  -- and a traceback of those places follows. A message that is not a string
2237
2217
  -- is left as it is, and a position that is not this bundle's (a library
2238
2218
  -- that raised its error with one already mapped) is left alone.
2219
+ --
2220
+ -- It is an \`xpcall\` handler, and a handler that fails loses the error
2221
+ -- altogether: Luau reports only "error in error handling". So the work is
2222
+ -- done under \`pcall\`, leaning on nothing it has not checked is there (an
2223
+ -- executor may lack \`debug.info\`, or refuse it), and an error it cannot
2224
+ -- map is still answered as Luau raised it, with why.
2239
2225
  fail = function(message)
2226
+ -- Taken first, before anything that can fail. Level 3 is the frame
2227
+ -- that raised: past \`pcall\` and this handler.
2228
+ local trace
2229
+ if debug ~= nil and debug.traceback ~= nil then
2230
+ local ok, text = pcall(debug.traceback, "", 3)
2231
+ if ok and type(text) == "string" then
2232
+ trace = text
2233
+ end
2234
+ end
2235
+ local ok, described = pcall(${G}.describe, message, trace)
2236
+ if ok then
2237
+ return described
2238
+ end
2239
+ if type(message) ~= "string" then
2240
+ return message
2241
+ end
2242
+ local why = type(described) == "string" and described or "unknown"
2243
+ return message .. "\\n(tilua could not map this error to the project's files: " .. why .. ")" .. (trace or "")
2244
+ end,
2245
+ -- \`fail\`'s work. \`trace\` is a traceback from where the error was
2246
+ -- raised, or nil when none could be taken.
2247
+ describe = function(message, trace)
2240
2248
  if type(message) ~= "string" then
2241
2249
  return message
2242
2250
  end
2243
2251
  local lines = ${G}.lines
2244
2252
  if lines == nil then
2245
- return debug.traceback(message, 2)
2253
+ return message .. (trace or "")
2246
2254
  end
2255
+ -- The bundle's name, as a position in a message spells it: where this
2256
+ -- function is, asked of the debug library by the function rather
2257
+ -- than by a level, which \`pcall\` would shift. Failing that, a message
2258
+ -- starts with the position it was raised at.
2247
2259
  local chunk
2248
- if debug.info ~= nil then
2249
- chunk = debug.info(1, "s")
2250
- else
2251
- chunk = debug.getinfo(1, "S").short_src
2260
+ if debug ~= nil and debug.info ~= nil then
2261
+ local found, source = pcall(debug.info, ${G}.describe, "s")
2262
+ if found and type(source) == "string" then
2263
+ chunk = source
2264
+ end
2265
+ end
2266
+ if chunk == nil and debug ~= nil and debug.getinfo ~= nil then
2267
+ local found, info = pcall(debug.getinfo, ${G}.describe, "S")
2268
+ if found and type(info) == "table" and type(info.short_src) == "string" then
2269
+ chunk = info.short_src
2270
+ end
2271
+ end
2272
+ if chunk == nil then
2273
+ chunk = message:match("^(.-):%d+:")
2274
+ end
2275
+ if chunk == nil then
2276
+ return message .. (trace or "")
2252
2277
  end
2253
2278
  local prefix = chunk:gsub("%p", "%%%0")
2254
2279
  local function place(line)
@@ -2262,7 +2287,7 @@ ${G} = {
2262
2287
  return place(line)
2263
2288
  end)
2264
2289
  local frames = {}
2265
- for frame in debug.traceback("", 2):gmatch("[^\\n]+") do
2290
+ for frame in (trace or ""):gmatch("[^\\n]+") do
2266
2291
  local line = frame:match(prefix .. ":(%d+)")
2267
2292
  local at = line ~= nil and place(line) or nil
2268
2293
  if at ~= nil then
@@ -2347,13 +2372,13 @@ function parseFile(file, diagnostics, directives) {
2347
2372
  return void 0;
2348
2373
  }
2349
2374
  try {
2350
- const program2 = (0, import_parser3.parse)(text);
2351
- const found = (0, import_parser3.directivesOf)(text);
2375
+ const program2 = (0, import_parser6.parse)(text);
2376
+ const found = (0, import_parser6.directivesOf)(text);
2352
2377
  directives.set(file, found);
2353
2378
  if (stamp !== void 0) parsedFiles.set(file, { stamp, program: program2, text, directives: found });
2354
2379
  return { program: program2, text };
2355
2380
  } catch (error) {
2356
- if (error instanceof import_parser3.ParseError || error instanceof import_parser3.LexError) {
2381
+ if (error instanceof import_parser6.ParseError || error instanceof import_parser6.LexError) {
2357
2382
  const { line, column } = error;
2358
2383
  diagnostics.push({ file, message: error.message, line, column, category: "syntax" });
2359
2384
  return void 0;
@@ -2375,7 +2400,7 @@ function parsedLibrary(file) {
2375
2400
  }
2376
2401
  const cached = libraryCache.get(file);
2377
2402
  if (cached && cached.stamp === stamp) return cached.program;
2378
- const program2 = (0, import_parser3.parse)((0, import_node_fs2.readFileSync)(file, "utf8"));
2403
+ const program2 = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(file, "utf8"));
2379
2404
  libraryCache.set(file, { stamp, program: program2 });
2380
2405
  return program2;
2381
2406
  }
@@ -2383,7 +2408,7 @@ var moduleAnalyses = /* @__PURE__ */ new Map();
2383
2408
  function analyzeModules(modules, config) {
2384
2409
  const out = [];
2385
2410
  const analyses = /* @__PURE__ */ new Map();
2386
- const libraries = config ? (0, import_parser3.resolveTypeLibraries)(config) : { files: [], lowerings: [], problems: [] };
2411
+ const libraries = config ? (0, import_parser6.resolveTypeLibraries)(config) : { files: [], lowerings: [], problems: [] };
2387
2412
  for (const p of libraries.problems) out.push({ file: p.file, message: p.message, line: p.line ?? 1, column: p.column ?? 1, category: "config" });
2388
2413
  const libs = libraries.files.map(parsedLibrary);
2389
2414
  const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
@@ -2403,14 +2428,14 @@ function analyzeModules(modules, config) {
2403
2428
  readWhile.set(file, remembered.read);
2404
2429
  return remembered.result;
2405
2430
  }
2406
- const scopes = (0, import_parser3.analyzeScopes)(program2, { builtinGlobals: globals, reportUndeclared: libs.length > 0 });
2431
+ const scopes = (0, import_parser6.analyzeScopes)(program2, { builtinGlobals: globals, reportUndeclared: libs.length > 0 });
2407
2432
  const said = [];
2408
2433
  for (const d of scopes.diagnostics) {
2409
2434
  if (d.kind !== "undeclared") continue;
2410
2435
  said.push({ file, message: d.message, line: d.node.line.start, column: d.node.column.start, category: "type" });
2411
2436
  }
2412
2437
  const before = partials;
2413
- const types = (0, import_parser3.analyzeTypes)(program2, scopes, {
2438
+ const types = (0, import_parser6.analyzeTypes)(program2, scopes, {
2414
2439
  libs,
2415
2440
  resolveModule: resolverFor(file),
2416
2441
  reportUnknownTypes: libs.length > 0
@@ -2433,7 +2458,7 @@ function analyzeModules(modules, config) {
2433
2458
  return result;
2434
2459
  };
2435
2460
  const resolverFor = (file) => (specifier) => {
2436
- const target = (0, import_parser3.resolveModulePath)(file, specifier, config);
2461
+ const target = (0, import_parser6.resolveModulePath)(file, specifier, config);
2437
2462
  if (!target) return void 0;
2438
2463
  const noteRead = () => {
2439
2464
  const read2 = readWhile.get(file) ?? /* @__PURE__ */ new Map();
@@ -2454,16 +2479,16 @@ function analyzeModules(modules, config) {
2454
2479
  let program2 = byFile.get(target)?.program;
2455
2480
  if (!program2) {
2456
2481
  try {
2457
- program2 = (0, import_parser3.parse)((0, import_node_fs2.readFileSync)(target, "utf8"));
2482
+ program2 = (0, import_parser6.parse)((0, import_node_fs2.readFileSync)(target, "utf8"));
2458
2483
  } catch {
2459
2484
  return void 0;
2460
2485
  }
2461
2486
  }
2462
2487
  inProgress.add(target);
2463
2488
  try {
2464
- const scopes = (0, import_parser3.analyzeScopes)(program2, { builtinGlobals: globals });
2465
- const types = byFile.has(target) ? analyzeModule(target, program2).types : (0, import_parser3.analyzeTypes)(program2, scopes, { libs, resolveModule: resolverFor(target), diagnostics: false });
2466
- const exports2 = (0, import_parser3.moduleExports)(program2, scopes, types, resolverFor(target));
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));
2467
2492
  exportsCache.set(target, exports2);
2468
2493
  noteRead();
2469
2494
  return exports2;
@@ -2498,35 +2523,35 @@ function allStatements(statements) {
2498
2523
  // src/compile.ts
2499
2524
  var import_node_fs3 = require("fs");
2500
2525
  var import_node_path3 = require("path");
2501
- var import_parser4 = require("@tilua/parser");
2526
+ var import_parser7 = require("@tilua/parser");
2502
2527
  var import_luau_parser3 = require("luau-parser");
2503
2528
  async function compile(source, config) {
2504
2529
  let program2;
2505
2530
  try {
2506
- program2 = (0, import_parser4.parse)(source);
2531
+ program2 = (0, import_parser7.parse)(source);
2507
2532
  } catch (error) {
2508
- if (error instanceof import_parser4.ParseError || error instanceof import_parser4.LexError) {
2533
+ if (error instanceof import_parser7.ParseError || error instanceof import_parser7.LexError) {
2509
2534
  const { line, column } = error;
2510
2535
  return { diagnostics: [{ message: error.message, line, column }] };
2511
2536
  }
2512
2537
  throw error;
2513
2538
  }
2514
- const scopes = (0, import_parser4.analyzeScopes)(program2);
2539
+ const scopes = (0, import_parser7.analyzeScopes)(program2);
2515
2540
  const diagnostics = scopes.diagnostics.map((d) => ({
2516
2541
  message: d.message,
2517
2542
  line: d.node.line.start,
2518
2543
  column: d.node.column.start
2519
2544
  }));
2520
2545
  const { config: resolved, problems } = resolveConfig(config, (0, import_node_path3.resolve)("main.tilua"));
2521
- const libraries = resolved ? (0, import_parser4.resolveTypeLibraries)(resolved) : { files: [], lowerings: [], problems: [] };
2546
+ const libraries = resolved ? (0, import_parser7.resolveTypeLibraries)(resolved) : { files: [], lowerings: [], problems: [] };
2522
2547
  const { lowerings, problems: loweringProblems } = await loadLowerings(libraries.lowerings);
2523
2548
  for (const p of [...problems, ...libraries.problems]) {
2524
2549
  diagnostics.push({ message: p.message, line: p.line ?? 1, column: p.column ?? 1 });
2525
2550
  }
2526
2551
  for (const p of loweringProblems) diagnostics.push({ message: p.message, line: 1, column: 1 });
2527
- const libs = libraries.files.map((file) => (0, import_parser4.parse)((0, import_node_fs3.readFileSync)(file, "utf8")));
2552
+ const libs = libraries.files.map((file) => (0, import_parser7.parse)((0, import_node_fs3.readFileSync)(file, "utf8")));
2528
2553
  const lowered = lower(program2, scopes, {
2529
- types: (0, import_parser4.analyzeTypes)(program2, scopes, { libs, diagnostics: false }),
2554
+ types: (0, import_parser7.analyzeTypes)(program2, scopes, { libs, diagnostics: false }),
2530
2555
  lowerings,
2531
2556
  target: resolved?.target,
2532
2557
  source
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  lower,
5
5
  program,
6
6
  resolveConfig
7
- } from "./chunk-X4DM2BX2.js";
7
+ } from "./chunk-VANZBLCL.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": "3.0.0",
3
+ "version": "3.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",
@@ -38,7 +38,7 @@
38
38
  "type": "module",
39
39
  "dependencies": {
40
40
  "luau-parser": "^1.0.3",
41
- "@tilua/parser": "^3.0.0"
41
+ "@tilua/parser": "^3.1.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^22.20.2",
@@ -52,3 +52,42 @@ function __NAME__.entries(known, open, t)
52
52
  end
53
53
  return out
54
54
  end
55
+
56
+ -- JavaScript's `Object` functions, as methods of the table they take first.
57
+ -- `known` and `open` are handed to every method here; these need neither.
58
+
59
+ -- The table's own member, not one its metatable's `__index` lends it.
60
+ function __NAME__.hasOwn(known, open, t, key)
61
+ return rawget(t, key) ~= nil
62
+ end
63
+
64
+ function __NAME__.hasOwnProperty(known, open, t, key)
65
+ return rawget(t, key) ~= nil
66
+ end
67
+
68
+ -- Each source's members onto `t`, later sources over earlier ones; a `nil`
69
+ -- source is skipped, as JavaScript skips `null`.
70
+ function __NAME__.assign(known, open, t, ...)
71
+ for i = 1, select("#", ...) do
72
+ local source = select(i, ...)
73
+ if source ~= nil then
74
+ for key, value in pairs(source) do
75
+ t[key] = value
76
+ end
77
+ end
78
+ end
79
+ return t
80
+ end
81
+
82
+ -- Luau freezes a table in place. Lua 5.1 has no way to, and the table is
83
+ -- answered as it is.
84
+ function __NAME__.freeze(known, open, t)
85
+ if table.freeze ~= nil and not table.isfrozen(t) then
86
+ table.freeze(t)
87
+ end
88
+ return t
89
+ end
90
+
91
+ function __NAME__.isFrozen(known, open, t)
92
+ return table.isfrozen ~= nil and table.isfrozen(t)
93
+ end