@tilua/compiler 3.1.0 → 5.0.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 +122 -12
- package/dist/{chunk-VANZBLCL.js → chunk-3J2QMA4W.js} +699 -135
- package/dist/cli.cjs +764 -178
- package/dist/cli.js +32 -9
- package/dist/index.cjs +721 -156
- package/dist/index.d.cts +44 -6
- package/dist/index.d.ts +44 -6
- package/dist/index.js +6 -4
- package/package.json +2 -2
- package/runtime/array.luau +17 -9
- package/runtime/console.luau +227 -0
- package/runtime/object.luau +4 -4
- package/runtime/result.luau +122 -0
- package/runtime/string.luau +2 -2
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>]
|
|
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
|
|
@@ -54,9 +59,10 @@ JavaScript module the build loads.
|
|
|
54
59
|
| tilua | Luau |
|
|
55
60
|
|---|---|
|
|
56
61
|
| `const { a, b } = value` | `local a, b = value.a, value.b` |
|
|
57
|
-
| `const [x, ...rest] = list` | `local x = list[1]; local rest = table.move(list, 2, #list, 1, {})` |
|
|
62
|
+
| `const [x, ...rest] = list` | `local x = list[1]; local rest = table.move(list, 2, #list, 1, {})` — `{}` written down as an array |
|
|
58
63
|
| `const { a = 1 } = t` | `local a = t.a; if a == nil then a = 1 end` |
|
|
59
|
-
| `[1, 2]` / `{ a: 1, b }` | `{ 1, 2 }` / `{ a = 1, b = b }` |
|
|
64
|
+
| `[1, 2]` / `{ a: 1, b }` | `tilua_asArray({ 1, 2 })` / `{ a = 1, b = b }` — see `typeof` below |
|
|
65
|
+
| `typeof v` | `tilua_typeof(v)`, or `tilua_typeof(v, "array")` where `v`'s type already says which table it is |
|
|
60
66
|
| `{ ...base, a: 1 }` / `[...xs, 1]` | a small helper copying the parts in order |
|
|
61
67
|
| `` `${a} any` `` | `("%s any"):format(tostring(a))` |
|
|
62
68
|
| `a?.b` | `if a == nil then nil else a.b` |
|
|
@@ -74,9 +80,52 @@ JavaScript module the build loads.
|
|
|
74
80
|
| `scriptArgs` | the chunk's own `...`, as `local scriptArgs = { ... }` |
|
|
75
81
|
| `f(a, ...xs)` | `table.unpack(xs)` in the last position |
|
|
76
82
|
| `f(...xs, a)` | the list built first, then `table.unpack` of it |
|
|
77
|
-
| `
|
|
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 |
|
|
78
90
|
| `names:filter(f)` | whatever the type library that declared `filter` says — see below |
|
|
79
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
|
+
|
|
109
|
+
## `typeof`, arrays and objects
|
|
110
|
+
|
|
111
|
+
Luau cannot tell an array from an object: both are tables, and an empty one
|
|
112
|
+
has nothing to go by. So every array the program makes is written down as
|
|
113
|
+
one where it is made — `[a, b]`, a rest parameter, `scriptArgs`, what
|
|
114
|
+
`:map`, `:filter`, `:keys` and the rest answer — in a table that holds its
|
|
115
|
+
keys weakly, one for the whole bundle. `tilua_asArray(t)` does that and hands
|
|
116
|
+
`t` back.
|
|
117
|
+
|
|
118
|
+
`typeof v` is Luau's `typeof` (Lua 5.1's `type`) for anything but a table.
|
|
119
|
+
For a table it answers, in order:
|
|
120
|
+
|
|
121
|
+
1. what `v`'s type says, where every table it can be is an array, or every
|
|
122
|
+
one an object — passed by the compiler, so nothing is looked up;
|
|
123
|
+
2. `"array"` if it was written down as one;
|
|
124
|
+
3. `"object"` if it has a metatable — an instance of a class;
|
|
125
|
+
4. `"array"` if it has a first element, and `"object"` otherwise: a table the
|
|
126
|
+
program did not make (one `JSONDecode` answered) is read by its shape, and
|
|
127
|
+
an empty one of those is an `"object"`.
|
|
128
|
+
|
|
80
129
|
## Classes
|
|
81
130
|
|
|
82
131
|
`class` is sugar over the Lua idiom, and it lowers to that idiom and nothing
|
|
@@ -148,10 +197,12 @@ the fast one.
|
|
|
148
197
|
|
|
149
198
|
## What a type library lowers
|
|
150
199
|
|
|
151
|
-
The compiler lowers tilua
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
200
|
+
The compiler lowers tilua, and Lua's standard library, which every file has:
|
|
201
|
+
`pcall` answers one value, `console:log` prints where it was called, `error`
|
|
202
|
+
names the project's file (runtime/console.luau, runtime/result.luau). What a
|
|
203
|
+
*library* gives a value, the library also says how to run: `names:first()` is
|
|
204
|
+
a call to a function because the library that declared `first` ships the Luau
|
|
205
|
+
behind it.
|
|
155
206
|
|
|
156
207
|
A library names a module in its package.json (`"tilua": { "lowering":
|
|
157
208
|
"lowering.mjs" }`) whose default export answers for a call. The contract is
|
|
@@ -185,9 +236,9 @@ argument (`arguments`: type, the code it was written as, and what a name was
|
|
|
185
236
|
declared as). An answer can `prepend` Luau expressions ahead of the written
|
|
186
237
|
arguments. `globalCall` and `globalValue` answer for a global, such as
|
|
187
238
|
`print(x)` or a bare `print`. A runtime can read `__LINES__` to reach the
|
|
188
|
-
bundle's line map
|
|
189
|
-
|
|
190
|
-
with
|
|
239
|
+
bundle's line map, and wraps a new array it answers with in `__ARRAY__(...)`. See the `@tilua/parser` README for the details.
|
|
240
|
+
The standard library's own lowering (src/standard.ts) is written with these
|
|
241
|
+
hooks too; `"noLib": true` leaves it out with the library.
|
|
191
242
|
|
|
192
243
|
## Errors at runtime
|
|
193
244
|
|
|
@@ -204,9 +255,13 @@ src/util.tilua:3: attempt to index nil with 'value'
|
|
|
204
255
|
```
|
|
205
256
|
|
|
206
257
|
A message that is not a string is raised unchanged. A position that is already
|
|
207
|
-
in the project's files, such as one from `error
|
|
258
|
+
in the project's files, such as one from the standard library's `error`, is
|
|
208
259
|
left as it is.
|
|
209
260
|
|
|
261
|
+
With several files each has its own line map, and a position is read by the
|
|
262
|
+
map of the file it names — matched by the end of that file's path, since the
|
|
263
|
+
Lua running it may call it by a full path, a relative one, or an Instance's.
|
|
264
|
+
|
|
210
265
|
## Modules
|
|
211
266
|
|
|
212
267
|
Roblox's `require` takes an Instance, so the bundle has its own. Each module
|
|
@@ -260,6 +315,61 @@ Modules follow ES module rules, including when they import each other:
|
|
|
260
315
|
- **Types only.** `import type` is erased whatever it names, and so is an
|
|
261
316
|
import used only as a type. A module reached only that way is left out of
|
|
262
317
|
the bundle and never runs.
|
|
318
|
+
- **`import()`.** `import("./shop")` is `G.import("src/shop")`, and `G.import`
|
|
319
|
+
is a wrapper around `G.require`: it answers the module's exports table — the
|
|
320
|
+
value `import * as Shop` names — where the code runs, loading the module if
|
|
321
|
+
nothing has yet. Nothing is awaited: Lua has no promises, and a module is
|
|
322
|
+
loaded by calling it. The path is a string literal; the module it names is
|
|
323
|
+
part of the bundle like any other.
|
|
324
|
+
|
|
325
|
+
## Several files
|
|
326
|
+
|
|
327
|
+
`"output"` in `tilua.config.json` (or `--output`, or `bundle({ output })`)
|
|
328
|
+
says how many files the project becomes:
|
|
329
|
+
|
|
330
|
+
| `output` | files |
|
|
331
|
+
|---|---|
|
|
332
|
+
| `"single"` (default) | one, holding every module |
|
|
333
|
+
| `"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 |
|
|
334
|
+
| `"modules"` | one per module, in the project's own tree |
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
tilua src/main.tilua --out build/main.luau --output chunks
|
|
338
|
+
# build/main.luau, build/main_src_shop.luau, ...
|
|
339
|
+
|
|
340
|
+
tilua src/main.tilua --out build --output modules
|
|
341
|
+
# build/src/main.luau, build/src/shop.luau, ...
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
With `"modules"`, `--out` is the folder the tree goes in — by default the
|
|
345
|
+
project's, so each `.luau` lands beside its `.tilua`. From the API,
|
|
346
|
+
`result.files` holds every file with the path it has to be written at, the
|
|
347
|
+
entry's first; `outFile` names the entry's file, which the others are named
|
|
348
|
+
after.
|
|
349
|
+
|
|
350
|
+
The module table is the same one, filled by more than one file. The entry's
|
|
351
|
+
file has the runtime; every other file is a function that adds its modules:
|
|
352
|
+
|
|
353
|
+
```lua
|
|
354
|
+
return function(G)
|
|
355
|
+
G.modules["src/shop"] = { load = function(exports) ... end }
|
|
356
|
+
end
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
`G.files` says which file each of those modules is in, and `require` loads
|
|
360
|
+
that file, once, the first time one of its modules is asked for — by an
|
|
361
|
+
`import()`, or by an `import` statement in a module that was itself loaded
|
|
362
|
+
late. So a file costs nothing until then.
|
|
363
|
+
|
|
364
|
+
How a file is loaded is the one thing the two targets do not share:
|
|
365
|
+
|
|
366
|
+
| `target` | loads a file with |
|
|
367
|
+
|---|---|
|
|
368
|
+
| `"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 |
|
|
369
|
+
| `"lua51"` | `loadfile(folder .. "main_src_shop.luau")()(G)`, where `folder` is the entry's own, read off `debug.getinfo` |
|
|
370
|
+
|
|
371
|
+
Either way the files have to stay where they were written, relative to the
|
|
372
|
+
entry's.
|
|
263
373
|
|
|
264
374
|
## Development
|
|
265
375
|
|