@tilua/compiler 8.0.0 → 8.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/README.md CHANGED
@@ -14,7 +14,7 @@ npx tilua src/main.tilua --out build/main.luau
14
14
 
15
15
  ```
16
16
  tilua <entry> [--out <file>] [--output single|chunks|modules] [--config <tilua.config.json>]
17
- [--noCheck]
17
+ [--noCheck] [--noLineMap]
18
18
  ```
19
19
 
20
20
  Every module the entry imports is bundled in. Problems are reported as
@@ -296,6 +296,16 @@ With several files each has its own line map, and a position is read by the
296
296
  map of the file it names — matched by the end of that file's path, since the
297
297
  Lua running it may call it by a full path, a relative one, or an Instance's.
298
298
 
299
+ The map is packed — the files it names once, then three base-64 VLQ numbers a
300
+ line, each counted from the line before — and read out the first time a line
301
+ is looked up, so a program that never fails never unpacks it.
302
+
303
+ For a release nobody will debug, `"lineMap": false` in `tilua.config.json`
304
+ (or `--noLineMap`, or `bundle({ lineMap: false })`) leaves out the map and
305
+ everything that reads it: the entry runs without `xpcall`, a callback handed
306
+ to Roblox is passed through unwrapped, and an error names the bundle's own
307
+ lines, as plain Luau would.
308
+
299
309
  ## Modules
300
310
 
301
311
  Roblox's `require` takes an Instance, so the bundle has its own. Each module
@@ -434,12 +444,12 @@ entry's.
434
444
  <!-- compatibility:start -->
435
445
  ## Compatibility
436
446
 
437
- `@tilua/compiler@8.0.0` works with these tilua packages. *Released with* is the
447
+ `@tilua/compiler@8.1.0` works with these tilua packages. *Released with* is the
438
448
  version it was built and tested against.
439
449
 
440
450
  | Package | Range | Released with | |
441
451
  | --- | --- | --- | --- |
442
- | `@tilua/parser` | `^8.0.0` | 8.0.0 | dependency |
452
+ | `@tilua/parser` | `^8.1.0` | 8.1.0 | dependency |
443
453
  <!-- compatibility:end -->
444
454
 
445
455
  ## Development
@@ -3101,10 +3101,33 @@ function buildLineMap(emitted, printed, origins) {
3101
3101
  }
3102
3102
  return map;
3103
3103
  }
3104
- function lineMapSource(map) {
3104
+ var VLQ_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3105
+ function vlq(n) {
3106
+ let value = n < 0 ? -n * 2 + 1 : n * 2;
3107
+ let out = "";
3108
+ do {
3109
+ const digit = value % 32;
3110
+ value = Math.floor(value / 32);
3111
+ out += VLQ_DIGITS[value > 0 ? digit + 32 : digit];
3112
+ } while (value > 0);
3113
+ return out;
3114
+ }
3115
+ function lineMapSource(map, unpack) {
3105
3116
  const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
3106
- const quoted = (s) => luauString3(s.replace(/\\/g, "/"));
3107
- return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
3117
+ const files = [];
3118
+ const indexes = /* @__PURE__ */ new Map();
3119
+ let data = "";
3120
+ let line = 0, file = 0, source = 0;
3121
+ for (const [at, origin] of entries) {
3122
+ const name = origin.file.replace(/\\/g, "/");
3123
+ if (!indexes.has(name)) indexes.set(name, files.push(name));
3124
+ const index2 = indexes.get(name);
3125
+ data += vlq(at - line) + vlq(index2 - file) + vlq(origin.line - source);
3126
+ line = at;
3127
+ file = index2;
3128
+ source = origin.line;
3129
+ }
3130
+ return `${unpack}({${files.map(luauString3).join(",")}},${luauString3(data)})`;
3108
3131
  }
3109
3132
 
3110
3133
  // src/bundle.ts
@@ -3152,6 +3175,7 @@ async function bundle(options) {
3152
3175
  const origins = /* @__PURE__ */ new WeakMap();
3153
3176
  const shared = new SharedRuntime(names);
3154
3177
  const shaking = options.treeShaking ?? config?.treeShaking ?? false;
3178
+ const mapping = options.lineMap ?? config?.lineMap ?? true;
3155
3179
  const resolveIn = (file) => (specifier) => {
3156
3180
  const found = resolveModulePath(file, specifier, config);
3157
3181
  return found !== void 0 && sources.has(found) ? found : void 0;
@@ -3190,8 +3214,8 @@ async function bundle(options) {
3190
3214
  import: importExpression,
3191
3215
  // One file has one line map. Several have one each, and a
3192
3216
  // module reads the one of the file it is in.
3193
- lines: mode === "single" ? `${G}.lines` : `${G}.maps[${luauString4(source.name)}]`,
3194
- fail: `${G}.fail`,
3217
+ lines: !mapping ? void 0 : mode === "single" ? `${G}.lines` : `${G}.maps[${luauString4(source.name)}]`,
3218
+ fail: mapping ? `${G}.fail` : void 0,
3195
3219
  arrays: `${G}.arrays`,
3196
3220
  resolve: (specifier, dynamic) => {
3197
3221
  const found = resolveModulePath(file, specifier, config);
@@ -3255,7 +3279,8 @@ async function bundle(options) {
3255
3279
  several,
3256
3280
  byPath,
3257
3281
  files: elsewhere,
3258
- imports: lowered.some((m) => m.dynamicImports.size > 0)
3282
+ imports: lowered.some((m) => m.dynamicImports.size > 0),
3283
+ mapping
3259
3284
  }));
3260
3285
  const assignment = program2.body.statements[1];
3261
3286
  if (usesScriptArgs) {
@@ -3272,21 +3297,23 @@ async function bundle(options) {
3272
3297
  program2.body.statements.unshift(...helpers.declare);
3273
3298
  const ok = names.fresh("ok");
3274
3299
  const result2 = names.fresh("result");
3275
- const entryStatements = parseLuau2(`
3300
+ const entryName = luauString4(sources.get(entry).name);
3301
+ const returnsExports = modules.get(entry).info.exports;
3302
+ const entryStatements = parseLuau2(!mapping ? `${returnsExports ? "return " : ""}${G}.require(${entryName})` : `
3276
3303
  local ${ok}, ${result2} = xpcall(function()
3277
- return ${G}.require(${luauString4(sources.get(entry).name)})
3304
+ return ${G}.require(${entryName})
3278
3305
  end, ${G}.fail)
3279
3306
  if not ${ok} then
3280
3307
  error(${result2}, 0)
3281
3308
  end
3282
- ${modules.get(entry).info.exports ? `return ${result2}` : ""}
3309
+ ${returnsExports ? `return ${result2}` : ""}
3283
3310
  `).body.statements;
3284
3311
  program2.body.statements.push(...entryStatements);
3285
- const map = buildLineMap(program2, parseLuau2(print(program2)), origins);
3286
- if (map.size || several) {
3312
+ const map = mapping ? buildLineMap(program2, parseLuau2(print(program2)), origins) : /* @__PURE__ */ new Map();
3313
+ if (mapping && (map.size || several)) {
3287
3314
  const mapStatements = [assign(
3288
3315
  [member(identifier(G), "lines")],
3289
- [parseExpressionFromSource2(lineMapSource(map))]
3316
+ [parseExpressionFromSource2(lineMapSource(map, `${G}.lineMap`))]
3290
3317
  )];
3291
3318
  if (several) mapStatements.push(...mapRegistration(G, layout[0], `${G}.lines`));
3292
3319
  program2.body.statements.splice(program2.body.statements.length - entryStatements.length, 0, ...mapStatements);
@@ -3304,8 +3331,11 @@ ${modules.get(entry).info.exports ? `return ${result2}` : ""}
3304
3331
  ));
3305
3332
  }
3306
3333
  const chunk = program([returns([functionExpression(functionBody([G], body))])]);
3307
- const lines = buildLineMap(chunk, parseLuau2(print(chunk)), origins);
3308
- body.push(...mapRegistration(G, file, lineMapSource(lines)), ...fileHelpers.define);
3334
+ if (mapping) {
3335
+ const lines = buildLineMap(chunk, parseLuau2(print(chunk)), origins);
3336
+ body.push(...mapRegistration(G, file, lineMapSource(lines, `${G}.lineMap`)));
3337
+ }
3338
+ body.push(...fileHelpers.define);
3309
3339
  files.push({ path: file.path, code: print(chunk) + "\n", modules: file.modules.map((m) => m.name) });
3310
3340
  }
3311
3341
  return { code: files[0].code, files, modules: moduleNames, diagnostics };
@@ -3403,7 +3433,7 @@ function loader(G, byPath) {
3403
3433
  require(file)(${G})`;
3404
3434
  }
3405
3435
  function runtime(G, options) {
3406
- const { several } = options;
3436
+ const { several, mapping } = options;
3407
3437
  const files = [...options.files].map(([name, file]) => `[${luauString4(name)}] = ${luauString4(file)}`).join(", ");
3408
3438
  return `
3409
3439
  local ${G}
@@ -3411,7 +3441,7 @@ ${G} = {
3411
3441
  modules = {},
3412
3442
  -- Every array the program made, so \`typeof\` can tell one from an object.
3413
3443
  -- The keys are held weakly: being written down keeps no array alive.
3414
- arrays = setmetatable({}, { __mode = "k" }),
3444
+ arrays = setmetatable({}, { __mode = "k" }),${mapping ? `
3415
3445
  -- Filled in just before the entry runs: bundle line -> { file, line }.
3416
3446
  lines = nil,
3417
3447
  -- What an error nothing caught says, in the project's files: the bundle's
@@ -3579,13 +3609,49 @@ ${several ? `
3579
3609
  end
3580
3610
  return mapped .. "\\n" .. table.concat(frames, "\\n")
3581
3611
  end,
3612
+ -- A line map as it is written: the files it names, and base-64 VLQ
3613
+ -- numbers, three to a line \u2014 how far down the bundle, which file, how far
3614
+ -- down that file, each counted from the line before. It is read out the
3615
+ -- first time a line is looked up, which is when something went wrong.
3616
+ lineMap = function(files, data)
3617
+ local map = {}
3618
+ return setmetatable(map, { __index = function(_, key)
3619
+ setmetatable(map, nil)
3620
+ local digits = {}
3621
+ for i = 1, 64 do
3622
+ digits[("${VLQ_DIGITS}"):byte(i)] = i - 1
3623
+ end
3624
+ local at = 1
3625
+ local function read()
3626
+ local value, scale = 0, 1
3627
+ repeat
3628
+ local digit = digits[data:byte(at)]
3629
+ at = at + 1
3630
+ value = value + digit % 32 * scale
3631
+ scale = scale * 32
3632
+ until digit < 32
3633
+ if value % 2 == 1 then
3634
+ return -math.floor(value / 2)
3635
+ end
3636
+ return math.floor(value / 2)
3637
+ end
3638
+ local line, file, source = 0, 0, 0
3639
+ while at <= #data do
3640
+ line = line + read()
3641
+ file = file + read()
3642
+ source = source + read()
3643
+ map[line] = { files[file], source }
3644
+ end
3645
+ return map[key]
3646
+ end })
3647
+ end,` : ""}
3582
3648
  records = {},${several ? `
3583
3649
  -- The file each module outside this one is in, as \`load\` is handed it.
3584
3650
  files = { ${files} },
3585
- loaded = {},
3651
+ loaded = {},${mapping ? `
3586
3652
  -- Every file's line map: by the module, and by the file's name.
3587
3653
  maps = {},
3588
- sources = {},
3654
+ sources = {},` : ""}
3589
3655
  -- Another of the bundle's files: a function that adds its modules here.
3590
3656
  load = function(file)
3591
3657
  if ${G}.loaded[file] then
package/dist/cli.cjs CHANGED
@@ -3070,10 +3070,33 @@ function buildLineMap(emitted, printed, origins) {
3070
3070
  }
3071
3071
  return map;
3072
3072
  }
3073
- function lineMapSource(map) {
3073
+ var VLQ_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3074
+ function vlq(n) {
3075
+ let value = n < 0 ? -n * 2 + 1 : n * 2;
3076
+ let out2 = "";
3077
+ do {
3078
+ const digit = value % 32;
3079
+ value = Math.floor(value / 32);
3080
+ out2 += VLQ_DIGITS[value > 0 ? digit + 32 : digit];
3081
+ } while (value > 0);
3082
+ return out2;
3083
+ }
3084
+ function lineMapSource(map, unpack) {
3074
3085
  const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
3075
- const quoted = (s) => (0, import_parser6.luauString)(s.replace(/\\/g, "/"));
3076
- return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
3086
+ const files = [];
3087
+ const indexes = /* @__PURE__ */ new Map();
3088
+ let data = "";
3089
+ let line = 0, file = 0, source = 0;
3090
+ for (const [at, origin] of entries) {
3091
+ const name = origin.file.replace(/\\/g, "/");
3092
+ if (!indexes.has(name)) indexes.set(name, files.push(name));
3093
+ const index2 = indexes.get(name);
3094
+ data += vlq(at - line) + vlq(index2 - file) + vlq(origin.line - source);
3095
+ line = at;
3096
+ file = index2;
3097
+ source = origin.line;
3098
+ }
3099
+ return `${unpack}({${files.map(import_parser6.luauString).join(",")}},${(0, import_parser6.luauString)(data)})`;
3077
3100
  }
3078
3101
 
3079
3102
  // src/lowering.ts
@@ -3148,6 +3171,7 @@ async function bundle(options) {
3148
3171
  const origins = /* @__PURE__ */ new WeakMap();
3149
3172
  const shared = new SharedRuntime(names);
3150
3173
  const shaking = options.treeShaking ?? config2?.treeShaking ?? false;
3174
+ const mapping = options.lineMap ?? config2?.lineMap ?? true;
3151
3175
  const resolveIn = (file) => (specifier) => {
3152
3176
  const found = (0, import_parser7.resolveModulePath)(file, specifier, config2);
3153
3177
  return found !== void 0 && sources.has(found) ? found : void 0;
@@ -3186,8 +3210,8 @@ async function bundle(options) {
3186
3210
  import: importExpression,
3187
3211
  // One file has one line map. Several have one each, and a
3188
3212
  // module reads the one of the file it is in.
3189
- lines: mode === "single" ? `${G}.lines` : `${G}.maps[${(0, import_parser7.luauString)(source.name)}]`,
3190
- fail: `${G}.fail`,
3213
+ lines: !mapping ? void 0 : mode === "single" ? `${G}.lines` : `${G}.maps[${(0, import_parser7.luauString)(source.name)}]`,
3214
+ fail: mapping ? `${G}.fail` : void 0,
3191
3215
  arrays: `${G}.arrays`,
3192
3216
  resolve: (specifier, dynamic) => {
3193
3217
  const found = (0, import_parser7.resolveModulePath)(file, specifier, config2);
@@ -3251,7 +3275,8 @@ async function bundle(options) {
3251
3275
  several,
3252
3276
  byPath,
3253
3277
  files: elsewhere,
3254
- imports: lowered.some((m) => m.dynamicImports.size > 0)
3278
+ imports: lowered.some((m) => m.dynamicImports.size > 0),
3279
+ mapping
3255
3280
  }));
3256
3281
  const assignment = program2.body.statements[1];
3257
3282
  if (usesScriptArgs) {
@@ -3268,21 +3293,23 @@ async function bundle(options) {
3268
3293
  program2.body.statements.unshift(...helpers.declare);
3269
3294
  const ok = names.fresh("ok");
3270
3295
  const result2 = names.fresh("result");
3271
- const entryStatements = (0, import_luau_parser2.parse)(`
3296
+ const entryName = (0, import_parser7.luauString)(sources.get(entry2).name);
3297
+ const returnsExports = modules.get(entry2).info.exports;
3298
+ const entryStatements = (0, import_luau_parser2.parse)(!mapping ? `${returnsExports ? "return " : ""}${G}.require(${entryName})` : `
3272
3299
  local ${ok}, ${result2} = xpcall(function()
3273
- return ${G}.require(${(0, import_parser7.luauString)(sources.get(entry2).name)})
3300
+ return ${G}.require(${entryName})
3274
3301
  end, ${G}.fail)
3275
3302
  if not ${ok} then
3276
3303
  error(${result2}, 0)
3277
3304
  end
3278
- ${modules.get(entry2).info.exports ? `return ${result2}` : ""}
3305
+ ${returnsExports ? `return ${result2}` : ""}
3279
3306
  `).body.statements;
3280
3307
  program2.body.statements.push(...entryStatements);
3281
- const map = buildLineMap(program2, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(program2)), origins);
3282
- if (map.size || several) {
3308
+ const map = mapping ? buildLineMap(program2, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(program2)), origins) : /* @__PURE__ */ new Map();
3309
+ if (mapping && (map.size || several)) {
3283
3310
  const mapStatements = [assign(
3284
3311
  [member(identifier(G), "lines")],
3285
- [(0, import_luau_parser2.parseExpressionFromSource)(lineMapSource(map))]
3312
+ [(0, import_luau_parser2.parseExpressionFromSource)(lineMapSource(map, `${G}.lineMap`))]
3286
3313
  )];
3287
3314
  if (several) mapStatements.push(...mapRegistration(G, layout[0], `${G}.lines`));
3288
3315
  program2.body.statements.splice(program2.body.statements.length - entryStatements.length, 0, ...mapStatements);
@@ -3300,8 +3327,11 @@ ${modules.get(entry2).info.exports ? `return ${result2}` : ""}
3300
3327
  ));
3301
3328
  }
3302
3329
  const chunk = program([returns([functionExpression(functionBody([G], body))])]);
3303
- const lines = buildLineMap(chunk, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(chunk)), origins);
3304
- body.push(...mapRegistration(G, file, lineMapSource(lines)), ...fileHelpers.define);
3330
+ if (mapping) {
3331
+ const lines = buildLineMap(chunk, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(chunk)), origins);
3332
+ body.push(...mapRegistration(G, file, lineMapSource(lines, `${G}.lineMap`)));
3333
+ }
3334
+ body.push(...fileHelpers.define);
3305
3335
  files.push({ path: file.path, code: (0, import_luau_parser2.print)(chunk) + "\n", modules: file.modules.map((m) => m.name) });
3306
3336
  }
3307
3337
  return { code: files[0].code, files, modules: moduleNames, diagnostics };
@@ -3399,7 +3429,7 @@ function loader(G, byPath) {
3399
3429
  require(file)(${G})`;
3400
3430
  }
3401
3431
  function runtime(G, options) {
3402
- const { several } = options;
3432
+ const { several, mapping } = options;
3403
3433
  const files = [...options.files].map(([name, file]) => `[${(0, import_parser7.luauString)(name)}] = ${(0, import_parser7.luauString)(file)}`).join(", ");
3404
3434
  return `
3405
3435
  local ${G}
@@ -3407,7 +3437,7 @@ ${G} = {
3407
3437
  modules = {},
3408
3438
  -- Every array the program made, so \`typeof\` can tell one from an object.
3409
3439
  -- The keys are held weakly: being written down keeps no array alive.
3410
- arrays = setmetatable({}, { __mode = "k" }),
3440
+ arrays = setmetatable({}, { __mode = "k" }),${mapping ? `
3411
3441
  -- Filled in just before the entry runs: bundle line -> { file, line }.
3412
3442
  lines = nil,
3413
3443
  -- What an error nothing caught says, in the project's files: the bundle's
@@ -3575,13 +3605,49 @@ ${several ? `
3575
3605
  end
3576
3606
  return mapped .. "\\n" .. table.concat(frames, "\\n")
3577
3607
  end,
3608
+ -- A line map as it is written: the files it names, and base-64 VLQ
3609
+ -- numbers, three to a line \u2014 how far down the bundle, which file, how far
3610
+ -- down that file, each counted from the line before. It is read out the
3611
+ -- first time a line is looked up, which is when something went wrong.
3612
+ lineMap = function(files, data)
3613
+ local map = {}
3614
+ return setmetatable(map, { __index = function(_, key)
3615
+ setmetatable(map, nil)
3616
+ local digits = {}
3617
+ for i = 1, 64 do
3618
+ digits[("${VLQ_DIGITS}"):byte(i)] = i - 1
3619
+ end
3620
+ local at = 1
3621
+ local function read()
3622
+ local value, scale = 0, 1
3623
+ repeat
3624
+ local digit = digits[data:byte(at)]
3625
+ at = at + 1
3626
+ value = value + digit % 32 * scale
3627
+ scale = scale * 32
3628
+ until digit < 32
3629
+ if value % 2 == 1 then
3630
+ return -math.floor(value / 2)
3631
+ end
3632
+ return math.floor(value / 2)
3633
+ end
3634
+ local line, file, source = 0, 0, 0
3635
+ while at <= #data do
3636
+ line = line + read()
3637
+ file = file + read()
3638
+ source = source + read()
3639
+ map[line] = { files[file], source }
3640
+ end
3641
+ return map[key]
3642
+ end })
3643
+ end,` : ""}
3578
3644
  records = {},${several ? `
3579
3645
  -- The file each module outside this one is in, as \`load\` is handed it.
3580
3646
  files = { ${files} },
3581
- loaded = {},
3647
+ loaded = {},${mapping ? `
3582
3648
  -- Every file's line map: by the module, and by the file's name.
3583
3649
  maps = {},
3584
- sources = {},
3650
+ sources = {},` : ""}
3585
3651
  -- Another of the bundle's files: a function that adds its modules here.
3586
3652
  load = function(file)
3587
3653
  if ${G}.loaded[file] then
@@ -3822,18 +3888,20 @@ function allStatements(statements) {
3822
3888
  }
3823
3889
 
3824
3890
  // src/cli.ts
3825
- var usage = "usage: tilua <entry> [--out <file>] [--output single|chunks|modules] [--config <tilua.config.json>] [--noCheck]";
3891
+ var usage = "usage: tilua <entry> [--out <file>] [--output single|chunks|modules] [--config <tilua.config.json>] [--noCheck] [--noLineMap]";
3826
3892
  var args = process.argv.slice(2);
3827
3893
  var entry;
3828
3894
  var out;
3829
3895
  var config;
3830
3896
  var typeCheck = true;
3897
+ var lineMap;
3831
3898
  var output;
3832
3899
  for (let i = 0; i < args.length; i++) {
3833
3900
  const arg = args[i];
3834
3901
  if (arg === "--out") out = args[++i];
3835
3902
  else if (arg === "--config") config = args[++i];
3836
3903
  else if (arg === "--noCheck") typeCheck = false;
3904
+ else if (arg === "--noLineMap") lineMap = false;
3837
3905
  else if (arg === "--target") {
3838
3906
  console.error(`--target is gone: the output runs on Lua 5.1 to 5.4, LuaJIT and Luau, and a library says what more its Lua has \u2014 add "luau" to the config's 'types'`);
3839
3907
  process.exit(2);
@@ -3863,6 +3931,7 @@ async function main(from) {
3863
3931
  config,
3864
3932
  typeCheck,
3865
3933
  output: mode,
3934
+ lineMap,
3866
3935
  outFile: entryFile !== void 0 ? (0, import_node_path3.basename)(entryFile) : void 0
3867
3936
  });
3868
3937
  for (const d of result2.diagnostics) {
package/dist/cli.js CHANGED
@@ -2,23 +2,25 @@
2
2
  import {
3
3
  bundle,
4
4
  resolveConfig
5
- } from "./chunk-TOP7NC3Z.js";
5
+ } from "./chunk-WOBHMGGC.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { mkdirSync, writeFileSync } from "fs";
9
9
  import { basename, dirname, join, relative, resolve } from "path";
10
- var usage = "usage: tilua <entry> [--out <file>] [--output single|chunks|modules] [--config <tilua.config.json>] [--noCheck]";
10
+ var usage = "usage: tilua <entry> [--out <file>] [--output single|chunks|modules] [--config <tilua.config.json>] [--noCheck] [--noLineMap]";
11
11
  var args = process.argv.slice(2);
12
12
  var entry;
13
13
  var out;
14
14
  var config;
15
15
  var typeCheck = true;
16
+ var lineMap;
16
17
  var output;
17
18
  for (let i = 0; i < args.length; i++) {
18
19
  const arg = args[i];
19
20
  if (arg === "--out") out = args[++i];
20
21
  else if (arg === "--config") config = args[++i];
21
22
  else if (arg === "--noCheck") typeCheck = false;
23
+ else if (arg === "--noLineMap") lineMap = false;
22
24
  else if (arg === "--target") {
23
25
  console.error(`--target is gone: the output runs on Lua 5.1 to 5.4, LuaJIT and Luau, and a library says what more its Lua has \u2014 add "luau" to the config's 'types'`);
24
26
  process.exit(2);
@@ -48,6 +50,7 @@ async function main(from) {
48
50
  config,
49
51
  typeCheck,
50
52
  output: mode,
53
+ lineMap,
51
54
  outFile: entryFile !== void 0 ? basename(entryFile) : void 0
52
55
  });
53
56
  for (const d of result.diagnostics) {
package/dist/index.cjs CHANGED
@@ -3093,10 +3093,33 @@ function buildLineMap(emitted, printed, origins) {
3093
3093
  }
3094
3094
  return map;
3095
3095
  }
3096
- function lineMapSource(map) {
3096
+ var VLQ_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3097
+ function vlq(n) {
3098
+ let value = n < 0 ? -n * 2 + 1 : n * 2;
3099
+ let out = "";
3100
+ do {
3101
+ const digit = value % 32;
3102
+ value = Math.floor(value / 32);
3103
+ out += VLQ_DIGITS[value > 0 ? digit + 32 : digit];
3104
+ } while (value > 0);
3105
+ return out;
3106
+ }
3107
+ function lineMapSource(map, unpack) {
3097
3108
  const entries = [...map.entries()].sort((a, b) => a[0] - b[0]);
3098
- const quoted = (s) => (0, import_parser6.luauString)(s.replace(/\\/g, "/"));
3099
- return `{${entries.map(([line, o]) => `[${line}]={${quoted(o.file)},${o.line}}`).join(",")}}`;
3109
+ const files = [];
3110
+ const indexes = /* @__PURE__ */ new Map();
3111
+ let data = "";
3112
+ let line = 0, file = 0, source = 0;
3113
+ for (const [at, origin] of entries) {
3114
+ const name = origin.file.replace(/\\/g, "/");
3115
+ if (!indexes.has(name)) indexes.set(name, files.push(name));
3116
+ const index2 = indexes.get(name);
3117
+ data += vlq(at - line) + vlq(index2 - file) + vlq(origin.line - source);
3118
+ line = at;
3119
+ file = index2;
3120
+ source = origin.line;
3121
+ }
3122
+ return `${unpack}({${files.map(import_parser6.luauString).join(",")}},${(0, import_parser6.luauString)(data)})`;
3100
3123
  }
3101
3124
 
3102
3125
  // src/lowering.ts
@@ -3171,6 +3194,7 @@ async function bundle(options) {
3171
3194
  const origins = /* @__PURE__ */ new WeakMap();
3172
3195
  const shared = new SharedRuntime(names);
3173
3196
  const shaking = options.treeShaking ?? config?.treeShaking ?? false;
3197
+ const mapping = options.lineMap ?? config?.lineMap ?? true;
3174
3198
  const resolveIn = (file) => (specifier) => {
3175
3199
  const found = (0, import_parser7.resolveModulePath)(file, specifier, config);
3176
3200
  return found !== void 0 && sources.has(found) ? found : void 0;
@@ -3209,8 +3233,8 @@ async function bundle(options) {
3209
3233
  import: importExpression,
3210
3234
  // One file has one line map. Several have one each, and a
3211
3235
  // module reads the one of the file it is in.
3212
- lines: mode === "single" ? `${G}.lines` : `${G}.maps[${(0, import_parser7.luauString)(source.name)}]`,
3213
- fail: `${G}.fail`,
3236
+ lines: !mapping ? void 0 : mode === "single" ? `${G}.lines` : `${G}.maps[${(0, import_parser7.luauString)(source.name)}]`,
3237
+ fail: mapping ? `${G}.fail` : void 0,
3214
3238
  arrays: `${G}.arrays`,
3215
3239
  resolve: (specifier, dynamic) => {
3216
3240
  const found = (0, import_parser7.resolveModulePath)(file, specifier, config);
@@ -3274,7 +3298,8 @@ async function bundle(options) {
3274
3298
  several,
3275
3299
  byPath,
3276
3300
  files: elsewhere,
3277
- imports: lowered.some((m) => m.dynamicImports.size > 0)
3301
+ imports: lowered.some((m) => m.dynamicImports.size > 0),
3302
+ mapping
3278
3303
  }));
3279
3304
  const assignment = program2.body.statements[1];
3280
3305
  if (usesScriptArgs) {
@@ -3291,21 +3316,23 @@ async function bundle(options) {
3291
3316
  program2.body.statements.unshift(...helpers.declare);
3292
3317
  const ok = names.fresh("ok");
3293
3318
  const result2 = names.fresh("result");
3294
- const entryStatements = (0, import_luau_parser2.parse)(`
3319
+ const entryName = (0, import_parser7.luauString)(sources.get(entry).name);
3320
+ const returnsExports = modules.get(entry).info.exports;
3321
+ const entryStatements = (0, import_luau_parser2.parse)(!mapping ? `${returnsExports ? "return " : ""}${G}.require(${entryName})` : `
3295
3322
  local ${ok}, ${result2} = xpcall(function()
3296
- return ${G}.require(${(0, import_parser7.luauString)(sources.get(entry).name)})
3323
+ return ${G}.require(${entryName})
3297
3324
  end, ${G}.fail)
3298
3325
  if not ${ok} then
3299
3326
  error(${result2}, 0)
3300
3327
  end
3301
- ${modules.get(entry).info.exports ? `return ${result2}` : ""}
3328
+ ${returnsExports ? `return ${result2}` : ""}
3302
3329
  `).body.statements;
3303
3330
  program2.body.statements.push(...entryStatements);
3304
- const map = buildLineMap(program2, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(program2)), origins);
3305
- if (map.size || several) {
3331
+ const map = mapping ? buildLineMap(program2, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(program2)), origins) : /* @__PURE__ */ new Map();
3332
+ if (mapping && (map.size || several)) {
3306
3333
  const mapStatements = [assign(
3307
3334
  [member(identifier(G), "lines")],
3308
- [(0, import_luau_parser2.parseExpressionFromSource)(lineMapSource(map))]
3335
+ [(0, import_luau_parser2.parseExpressionFromSource)(lineMapSource(map, `${G}.lineMap`))]
3309
3336
  )];
3310
3337
  if (several) mapStatements.push(...mapRegistration(G, layout[0], `${G}.lines`));
3311
3338
  program2.body.statements.splice(program2.body.statements.length - entryStatements.length, 0, ...mapStatements);
@@ -3323,8 +3350,11 @@ ${modules.get(entry).info.exports ? `return ${result2}` : ""}
3323
3350
  ));
3324
3351
  }
3325
3352
  const chunk = program([returns([functionExpression(functionBody([G], body))])]);
3326
- const lines = buildLineMap(chunk, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(chunk)), origins);
3327
- body.push(...mapRegistration(G, file, lineMapSource(lines)), ...fileHelpers.define);
3353
+ if (mapping) {
3354
+ const lines = buildLineMap(chunk, (0, import_luau_parser2.parse)((0, import_luau_parser2.print)(chunk)), origins);
3355
+ body.push(...mapRegistration(G, file, lineMapSource(lines, `${G}.lineMap`)));
3356
+ }
3357
+ body.push(...fileHelpers.define);
3328
3358
  files.push({ path: file.path, code: (0, import_luau_parser2.print)(chunk) + "\n", modules: file.modules.map((m) => m.name) });
3329
3359
  }
3330
3360
  return { code: files[0].code, files, modules: moduleNames, diagnostics };
@@ -3422,7 +3452,7 @@ function loader(G, byPath) {
3422
3452
  require(file)(${G})`;
3423
3453
  }
3424
3454
  function runtime(G, options) {
3425
- const { several } = options;
3455
+ const { several, mapping } = options;
3426
3456
  const files = [...options.files].map(([name, file]) => `[${(0, import_parser7.luauString)(name)}] = ${(0, import_parser7.luauString)(file)}`).join(", ");
3427
3457
  return `
3428
3458
  local ${G}
@@ -3430,7 +3460,7 @@ ${G} = {
3430
3460
  modules = {},
3431
3461
  -- Every array the program made, so \`typeof\` can tell one from an object.
3432
3462
  -- The keys are held weakly: being written down keeps no array alive.
3433
- arrays = setmetatable({}, { __mode = "k" }),
3463
+ arrays = setmetatable({}, { __mode = "k" }),${mapping ? `
3434
3464
  -- Filled in just before the entry runs: bundle line -> { file, line }.
3435
3465
  lines = nil,
3436
3466
  -- What an error nothing caught says, in the project's files: the bundle's
@@ -3598,13 +3628,49 @@ ${several ? `
3598
3628
  end
3599
3629
  return mapped .. "\\n" .. table.concat(frames, "\\n")
3600
3630
  end,
3631
+ -- A line map as it is written: the files it names, and base-64 VLQ
3632
+ -- numbers, three to a line \u2014 how far down the bundle, which file, how far
3633
+ -- down that file, each counted from the line before. It is read out the
3634
+ -- first time a line is looked up, which is when something went wrong.
3635
+ lineMap = function(files, data)
3636
+ local map = {}
3637
+ return setmetatable(map, { __index = function(_, key)
3638
+ setmetatable(map, nil)
3639
+ local digits = {}
3640
+ for i = 1, 64 do
3641
+ digits[("${VLQ_DIGITS}"):byte(i)] = i - 1
3642
+ end
3643
+ local at = 1
3644
+ local function read()
3645
+ local value, scale = 0, 1
3646
+ repeat
3647
+ local digit = digits[data:byte(at)]
3648
+ at = at + 1
3649
+ value = value + digit % 32 * scale
3650
+ scale = scale * 32
3651
+ until digit < 32
3652
+ if value % 2 == 1 then
3653
+ return -math.floor(value / 2)
3654
+ end
3655
+ return math.floor(value / 2)
3656
+ end
3657
+ local line, file, source = 0, 0, 0
3658
+ while at <= #data do
3659
+ line = line + read()
3660
+ file = file + read()
3661
+ source = source + read()
3662
+ map[line] = { files[file], source }
3663
+ end
3664
+ return map[key]
3665
+ end })
3666
+ end,` : ""}
3601
3667
  records = {},${several ? `
3602
3668
  -- The file each module outside this one is in, as \`load\` is handed it.
3603
3669
  files = { ${files} },
3604
- loaded = {},
3670
+ loaded = {},${mapping ? `
3605
3671
  -- Every file's line map: by the module, and by the file's name.
3606
3672
  maps = {},
3607
- sources = {},
3673
+ sources = {},` : ""}
3608
3674
  -- Another of the bundle's files: a function that adds its modules here.
3609
3675
  load = function(file)
3610
3676
  if ${G}.loaded[file] then
package/dist/index.d.cts CHANGED
@@ -22,6 +22,9 @@ interface TiluaConfigJson {
22
22
  noLib?: boolean;
23
23
  /** Leave out of the bundle what the entry never reaches (`treeShake`). */
24
24
  treeShaking?: boolean;
25
+ /** Write the map from the bundle's lines back to the project's, which
26
+ * error reports and `console` read. Default: true. */
27
+ lineMap?: boolean;
25
28
  }
26
29
  /** A config file's path, or the config itself. Relative paths — to a config
27
30
  * file, and inside an inline config — resolve from `cwd`. */
@@ -51,6 +54,10 @@ interface BundleOptions {
51
54
  /** Leave out what the entry never reaches. Overrides the config's
52
55
  * `treeShaking`; off without either. */
53
56
  readonly treeShaking?: boolean;
57
+ /** Write the map from the bundle's lines back to the project's. Overrides
58
+ * the config's `lineMap`; on without either. Without it an error names
59
+ * the bundle's lines, and the entry does not run under `xpcall`. */
60
+ readonly lineMap?: boolean;
54
61
  /** What the entry's file is called: `main.luau`. The other files of
55
62
  * `"chunks"` are named after it (`main_shop.luau`), and every file takes
56
63
  * its extension. Default: the entry's own name, with `.luau`. */
package/dist/index.d.ts CHANGED
@@ -22,6 +22,9 @@ interface TiluaConfigJson {
22
22
  noLib?: boolean;
23
23
  /** Leave out of the bundle what the entry never reaches (`treeShake`). */
24
24
  treeShaking?: boolean;
25
+ /** Write the map from the bundle's lines back to the project's, which
26
+ * error reports and `console` read. Default: true. */
27
+ lineMap?: boolean;
25
28
  }
26
29
  /** A config file's path, or the config itself. Relative paths — to a config
27
30
  * file, and inside an inline config — resolve from `cwd`. */
@@ -51,6 +54,10 @@ interface BundleOptions {
51
54
  /** Leave out what the entry never reaches. Overrides the config's
52
55
  * `treeShaking`; off without either. */
53
56
  readonly treeShaking?: boolean;
57
+ /** Write the map from the bundle's lines back to the project's. Overrides
58
+ * the config's `lineMap`; on without either. Without it an error names
59
+ * the bundle's lines, and the entry does not run under `xpcall`. */
60
+ readonly lineMap?: boolean;
54
61
  /** What the entry's file is called: `main.luau`. The other files of
55
62
  * `"chunks"` are named after it (`main_shop.luau`), and every file takes
56
63
  * its extension. Default: the entry's own name, with `.luau`. */
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  lower,
5
5
  program,
6
6
  resolveConfig
7
- } from "./chunk-TOP7NC3Z.js";
7
+ } from "./chunk-WOBHMGGC.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": "8.0.0",
3
+ "version": "8.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": "^8.0.0"
41
+ "@tilua/parser": "^8.1.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^22.20.2",