@tilua/compiler 4.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,8 @@ npx tilua src/main.tilua --out build/main.luau
11
11
  ```
12
12
 
13
13
  ```
14
- tilua <entry> [--out <file>] [--config <tilua.config.json>] [--noCheck] [--target luau|lua51]
14
+ tilua <entry> [--out <file>] [--output single|chunks|modules] [--config <tilua.config.json>]
15
+ [--noCheck] [--target luau|lua51]
15
16
  ```
16
17
 
17
18
  Every module the entry imports is bundled in. Problems are reported as
@@ -24,6 +25,9 @@ Without `--out`, the entry's own path with `.luau` is written. Without
24
25
  [`@tilua/parser`](https://www.npmjs.com/package/@tilua/parser) README for what
25
26
  goes in it.
26
27
 
28
+ `--output` (or `"output"` in the config) makes the bundle more than one file —
29
+ see [Several files](#several-files).
30
+
27
31
  ## API
28
32
 
29
33
  ```ts
@@ -35,6 +39,7 @@ const result = await bundle({
35
39
  config: { types: ["roblox"], paths: { "@shared/*": ["src/shared/*"] } },
36
40
  })
37
41
  result.code // the Luau bundle, unless a module failed
42
+ result.files // [{ path, code, modules }] — one, unless `output` says otherwise
38
43
  result.diagnostics // { file, line, column, message, category: "syntax" | "module" | "type" | "config" }
39
44
 
40
45
  // One file, no imports. The config is optional here too, and brings the same
@@ -75,9 +80,32 @@ JavaScript module the build loads.
75
80
  | `scriptArgs` | the chunk's own `...`, as `local scriptArgs = { ... }` |
76
81
  | `f(a, ...xs)` | `table.unpack(xs)` in the last position |
77
82
  | `f(...xs, a)` | the list built first, then `table.unpack` of it |
78
- | `x as T`, `x satisfies T`, types, `declare` | removed |
83
+ | `import("./shop")` | `G.import("src/shop")` — the bundle's `require`, wrapped |
84
+ | `a ?? b` | `if a ~= nil then a else b` for a name; otherwise a function that reads `a` once |
85
+ | `n++` / `n--` | `n += 1` / `n -= 1` |
86
+ | `"key" in t` | `(t["key"] ~= nil)` |
87
+ | `v instanceof Dog` | `tilua_instanceof(v, Dog)` — walks `v.ClassObject`, then each `.ParentClass` |
88
+ | `{ a: 1, get b() { … }, set b(v) { … } }` | `tilua_accessors({ a = 1 }, { b = … }, { b = … })` — a metatable runs the accessors |
89
+ | `x as T`, `x satisfies T`, types, `interface`, `export type { … }`, `declare` | removed |
79
90
  | `names:filter(f)` | whatever the type library that declared `filter` says — see below |
80
91
 
92
+ ## Lua 5.1
93
+
94
+ `"target": "lua51"` (or `--target lua51`) writes what stock Lua 5.1 runs. On
95
+ top of the table above:
96
+
97
+ | Luau | Lua 5.1 |
98
+ |---|---|
99
+ | `a += b`, `a // b`, `continue`, `if c then a else b`, `1_000`, `0b101` | assignments, `math.floor`, a `break` out of a one-pass `repeat`, `and`/`or` or a call, plain numbers |
100
+ | `for _, item in xs do` | `ipairs(xs)` where `xs` is an array by its type; otherwise `tilua_each(xs)`, which walks a table as Luau would — its `__iter` if it has one, `next` if not |
101
+ | `if a == nil then nil else a.b` (`a?.b`) | the function form, which keeps a `false` |
102
+ | `table.move`, `table.unpack` | `tilua_move`, `unpack` |
103
+ | `@native` and other attributes | dropped |
104
+
105
+ An object's `freeze` does nothing there and `isFrozen` answers `false`: Lua 5.1
106
+ cannot freeze a table. `npm test` with `LUA51` naming a Lua 5.1 interpreter
107
+ builds a program for both targets and checks they print the same.
108
+
81
109
  ## `typeof`, arrays and objects
82
110
 
83
111
  Luau cannot tell an array from an object: both are tables, and an empty one
@@ -152,7 +180,9 @@ another, and one the class writes itself replaces the copy.
152
180
  Two links come with every class, and they cost an instance nothing because
153
181
  they live on the class table: `ClassObject`, which an instance reads through
154
182
  its metatable to reach its own class, and `ParentClass`, which a class reads
155
- to reach the one it extends.
183
+ to reach the one it extends. The class table holds its `name` too
184
+ (`tilua_class(base, "Dog")`), so `rex.ClassObject.name` says what built an
185
+ instance.
156
186
 
157
187
  A class written as a value (`const Counter = class { … }`) is the same code,
158
188
  inside a function that runs where the class is written and hands the table
@@ -169,10 +199,12 @@ the fast one.
169
199
 
170
200
  ## What a type library lowers
171
201
 
172
- The compiler lowers tilua. What a *library* gives a value, the library also
173
- says how to run: `names:filter(f)` is a call to a function because
174
- [`@tilua-types/lua`](https://www.npmjs.com/package/@tilua-types/lua) declares
175
- the method and ships the Luau behind it.
202
+ The compiler lowers tilua, and Lua's standard library, which every file has:
203
+ `pcall` answers one value, `console:log` prints where it was called, `error`
204
+ names the project's file (runtime/console.luau, runtime/result.luau). What a
205
+ *library* gives a value, the library also says how to run: `names:first()` is
206
+ a call to a function because the library that declared `first` ships the Luau
207
+ behind it.
176
208
 
177
209
  A library names a module in its package.json (`"tilua": { "lowering":
178
210
  "lowering.mjs" }`) whose default export answers for a call. The contract is
@@ -207,8 +239,8 @@ declared as). An answer can `prepend` Luau expressions ahead of the written
207
239
  arguments. `globalCall` and `globalValue` answer for a global, such as
208
240
  `print(x)` or a bare `print`. A runtime can read `__LINES__` to reach the
209
241
  bundle's line map, and wraps a new array it answers with in `__ARRAY__(...)`. See the `@tilua/parser` README for the details.
210
- `console:log` is not built into the compiler: `@tilua-types/lua` implements it
211
- with these hooks.
242
+ The standard library's own lowering (src/standard.ts) is written with these
243
+ hooks too; `"noLib": true` leaves it out with the library.
212
244
 
213
245
  ## Errors at runtime
214
246
 
@@ -225,9 +257,13 @@ src/util.tilua:3: attempt to index nil with 'value'
225
257
  ```
226
258
 
227
259
  A message that is not a string is raised unchanged. A position that is already
228
- in the project's files, such as one from `error` in `@tilua-types/lua`, is
260
+ in the project's files, such as one from the standard library's `error`, is
229
261
  left as it is.
230
262
 
263
+ With several files each has its own line map, and a position is read by the
264
+ map of the file it names — matched by the end of that file's path, since the
265
+ Lua running it may call it by a full path, a relative one, or an Instance's.
266
+
231
267
  ## Modules
232
268
 
233
269
  Roblox's `require` takes an Instance, so the bundle has its own. Each module
@@ -281,6 +317,61 @@ Modules follow ES module rules, including when they import each other:
281
317
  - **Types only.** `import type` is erased whatever it names, and so is an
282
318
  import used only as a type. A module reached only that way is left out of
283
319
  the bundle and never runs.
320
+ - **`import()`.** `import("./shop")` is `G.import("src/shop")`, and `G.import`
321
+ is a wrapper around `G.require`: it answers the module's exports table — the
322
+ value `import * as Shop` names — where the code runs, loading the module if
323
+ nothing has yet. Nothing is awaited: Lua has no promises, and a module is
324
+ loaded by calling it. The path is a string literal; the module it names is
325
+ part of the bundle like any other.
326
+
327
+ ## Several files
328
+
329
+ `"output"` in `tilua.config.json` (or `--output`, or `bundle({ output })`)
330
+ says how many files the project becomes:
331
+
332
+ | `output` | files |
333
+ |---|---|
334
+ | `"single"` (default) | one, holding every module |
335
+ | `"chunks"` | the entry's, holding what it reaches through `import` statements; one for each `import()` that leads somewhere else, holding what only it reaches; and one for what several of those share |
336
+ | `"modules"` | one per module, in the project's own tree |
337
+
338
+ ```
339
+ tilua src/main.tilua --out build/main.luau --output chunks
340
+ # build/main.luau, build/main_src_shop.luau, ...
341
+
342
+ tilua src/main.tilua --out build --output modules
343
+ # build/src/main.luau, build/src/shop.luau, ...
344
+ ```
345
+
346
+ With `"modules"`, `--out` is the folder the tree goes in — by default the
347
+ project's, so each `.luau` lands beside its `.tilua`. From the API,
348
+ `result.files` holds every file with the path it has to be written at, the
349
+ entry's first; `outFile` names the entry's file, which the others are named
350
+ after.
351
+
352
+ The module table is the same one, filled by more than one file. The entry's
353
+ file has the runtime; every other file is a function that adds its modules:
354
+
355
+ ```lua
356
+ return function(G)
357
+ G.modules["src/shop"] = { load = function(exports) ... end }
358
+ end
359
+ ```
360
+
361
+ `G.files` says which file each of those modules is in, and `require` loads
362
+ that file, once, the first time one of its modules is asked for — by an
363
+ `import()`, or by an `import` statement in a module that was itself loaded
364
+ late. So a file costs nothing until then.
365
+
366
+ How a file is loaded is the one thing the two targets do not share:
367
+
368
+ | `target` | loads a file with |
369
+ |---|---|
370
+ | `"luau"` | `require("./main_src_shop")(G)` — Luau's `require` by path, read from the entry's own file. In Roblox each file is a ModuleScript beside the entry's |
371
+ | `"lua51"` | `loadfile(folder .. "main_src_shop.luau")()(G)`, where `folder` is the entry's own, read off `debug.getinfo` |
372
+
373
+ Either way the files have to stay where they were written, relative to the
374
+ entry's.
284
375
 
285
376
  ## Development
286
377