@gwigz/slua-tstl-plugin 1.0.0 → 1.2.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 +234 -16
- package/dist/constants.d.ts +25 -0
- package/dist/constants.js +50 -0
- package/dist/define.d.ts +19 -0
- package/dist/define.js +83 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.js +341 -662
- package/dist/lua-ast-walk.d.ts +10 -0
- package/dist/lua-ast-walk.js +422 -0
- package/dist/lua-transforms.d.ts +41 -0
- package/dist/lua-transforms.js +302 -0
- package/dist/optimize.d.ts +31 -0
- package/dist/optimize.js +106 -0
- package/dist/transforms.d.ts +7 -0
- package/dist/transforms.js +195 -0
- package/dist/utils.d.ts +130 -0
- package/dist/utils.js +412 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
[TypeScriptToLua](https://typescripttolua.github.io) plugin to provide better DX with SLua types.
|
|
4
4
|
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Add the plugin to `tstl.luaPlugins` in your `tsconfig.json`:
|
|
8
|
+
|
|
9
|
+
```jsonc
|
|
10
|
+
{
|
|
11
|
+
"tstl": {
|
|
12
|
+
"luaTarget": "Luau",
|
|
13
|
+
"luaPlugins": [{ "name": "@gwigz/slua-tstl-plugin" }],
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
To enable output optimizations, pass `optimize: true` for all flags, or pick individual ones:
|
|
19
|
+
|
|
20
|
+
```jsonc
|
|
21
|
+
{
|
|
22
|
+
"tstl": {
|
|
23
|
+
"luaPlugins": [
|
|
24
|
+
// all optimizations
|
|
25
|
+
{ "name": "@gwigz/slua-tstl-plugin", "optimize": true },
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```jsonc
|
|
32
|
+
{
|
|
33
|
+
"tstl": {
|
|
34
|
+
"luaPlugins": [
|
|
35
|
+
// pick individual optimizations
|
|
36
|
+
{
|
|
37
|
+
"name": "@gwigz/slua-tstl-plugin",
|
|
38
|
+
"optimize": {
|
|
39
|
+
"compoundAssignment": true,
|
|
40
|
+
"shortenTemps": true,
|
|
41
|
+
"inlineLocals": true,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
5
49
|
## What it does
|
|
6
50
|
|
|
7
51
|
- Translates TypeScript patterns to native Luau/LSL equivalents (see below)
|
|
@@ -34,24 +78,26 @@ For SL-typed JSON (preserving vector/quaternion/uuid), use `lljson.slencode`/`ll
|
|
|
34
78
|
|
|
35
79
|
String methods are translated to LSL `ll.*` functions or Luau `string.*` stdlib calls:
|
|
36
80
|
|
|
37
|
-
| TypeScript | Lua output
|
|
38
|
-
| ---------------------- |
|
|
39
|
-
| `str.toUpperCase()` | `ll.ToUpper(str)`
|
|
40
|
-
| `str.toLowerCase()` | `ll.ToLower(str)`
|
|
41
|
-
| `str.trim()` | `ll.StringTrim(str, STRING_TRIM)`
|
|
42
|
-
| `str.trimStart()` | `ll.StringTrim(str, STRING_TRIM_HEAD)`
|
|
43
|
-
| `str.trimEnd()` | `ll.StringTrim(str, STRING_TRIM_TAIL)`
|
|
44
|
-
| `str.indexOf(x)` | `(string.find(str, x, 1, true) or 0) - 1`
|
|
45
|
-
| `str.
|
|
46
|
-
| `str.
|
|
47
|
-
| `str.
|
|
48
|
-
| `str.
|
|
49
|
-
| `str.
|
|
50
|
-
| `str.substring(
|
|
51
|
-
| `str.
|
|
81
|
+
| TypeScript | Lua output |
|
|
82
|
+
| ---------------------- | ------------------------------------------------ |
|
|
83
|
+
| `str.toUpperCase()` | `ll.ToUpper(str)` |
|
|
84
|
+
| `str.toLowerCase()` | `ll.ToLower(str)` |
|
|
85
|
+
| `str.trim()` | `ll.StringTrim(str, STRING_TRIM)` |
|
|
86
|
+
| `str.trimStart()` | `ll.StringTrim(str, STRING_TRIM_HEAD)` |
|
|
87
|
+
| `str.trimEnd()` | `ll.StringTrim(str, STRING_TRIM_TAIL)` |
|
|
88
|
+
| `str.indexOf(x)` | `(string.find(str, x, 1, true) or 0) - 1` |
|
|
89
|
+
| `str.indexOf(x, from)` | `(string.find(str, x, from + 1, true) or 0) - 1` |
|
|
90
|
+
| `str.includes(x)` | `string.find(str, x, 1, true) ~= nil` |
|
|
91
|
+
| `str.startsWith(x)` | `string.find(str, x, 1, true) == 1` |
|
|
92
|
+
| `str.split(sep)` | `string.split(str, sep)` |
|
|
93
|
+
| `str.repeat(n)` | `string.rep(str, n)` |
|
|
94
|
+
| `str.substring(start)` | `string.sub(str, start + 1)` |
|
|
95
|
+
| `str.substring(s, e)` | `string.sub(str, s + 1, e)` |
|
|
96
|
+
| `str.replace(a, b)` | `ll.ReplaceSubString(str, a, b, 1)` |
|
|
97
|
+
| `str.replaceAll(a, b)` | `ll.ReplaceSubString(str, a, b, 0)` |
|
|
52
98
|
|
|
53
99
|
> [!NOTE]
|
|
54
|
-
> `str.indexOf(x, fromIndex)`
|
|
100
|
+
> `str.indexOf(x, fromIndex)` adjusts the `fromIndex` to 1-based (constant-folded for literals). `str.startsWith(x, position)` with a second argument falls through to TSTL's default handling. Similarly, `str.split()` with no separator is not transformed.
|
|
55
101
|
|
|
56
102
|
### Array methods
|
|
57
103
|
|
|
@@ -134,6 +180,178 @@ This only applies when the argument is directly a `/` expression. `Math.floor(x)
|
|
|
134
180
|
> [!WARNING]
|
|
135
181
|
> JavaScript integer truncation idioms `~~x` and `x | 0` do **not** map cleanly to Luau. `~~x` emits `bit32.bnot(bit32.bnot(x))` and `x | 0` emits `bit32.bor(x, 0)`, neither of which preserves correct semantics for negative numbers (the `bit32` library operates on unsigned 32-bit integers). Use `math.floor(x)` for floor truncation instead.
|
|
136
182
|
|
|
183
|
+
### Passthrough arrow closures
|
|
184
|
+
|
|
185
|
+
Zero-parameter arrow functions that just call another zero-parameter function are collapsed to a direct function reference:
|
|
186
|
+
|
|
187
|
+
| TypeScript | Lua output |
|
|
188
|
+
| ------------------------------------------- | ----------------------------------- |
|
|
189
|
+
| `LLTimers.once(1, () => patchNext())` | `LLTimers:once(1, patchNext)` |
|
|
190
|
+
| `LLEvents.on("on_rez", () => refreshUrl())` | `LLEvents:on("on_rez", refreshUrl)` |
|
|
191
|
+
|
|
192
|
+
This applies when:
|
|
193
|
+
|
|
194
|
+
- The arrow has zero parameters
|
|
195
|
+
- The body is a single call with zero arguments
|
|
196
|
+
- The callee is a simple identifier (not a method call)
|
|
197
|
+
- The callee's type signature has zero parameters (so extra args from the caller are harmlessly ignored)
|
|
198
|
+
|
|
199
|
+
## Optimizations
|
|
200
|
+
|
|
201
|
+
Pass `optimize: true` to enable all optimizations, or pass an object to pick individual flags. All flags default to `false` when not specified.
|
|
202
|
+
|
|
203
|
+
### `filter`
|
|
204
|
+
|
|
205
|
+
Inlines `arr.filter(cb)` as an `ipairs` loop instead of pulling in `__TS__ArrayFilter`.
|
|
206
|
+
|
|
207
|
+
Automatically disabled for files with more than one `.filter()` call, where the shared helper is results in a smaller script.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
const result = arr.filter((x) => x > 0)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
```lua
|
|
214
|
+
local function ____opt_fn_0(x)
|
|
215
|
+
return x > 0
|
|
216
|
+
end
|
|
217
|
+
local ____opt_0 = {}
|
|
218
|
+
for _, ____opt_v_0 in ipairs(arr) do
|
|
219
|
+
if ____opt_fn_0(____opt_v_0) then
|
|
220
|
+
____opt_0[#____opt_0 + 1] = ____opt_v_0
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
local result = ____opt_0
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### `compoundAssignment`
|
|
227
|
+
|
|
228
|
+
Rewrites self-reassignment arithmetic to Luau compound assignment operators.
|
|
229
|
+
|
|
230
|
+
| TypeScript | Lua output |
|
|
231
|
+
| ------------ | ---------- |
|
|
232
|
+
| `x = x + n` | `x += n` |
|
|
233
|
+
| `x = x - 1` | `x -= 1` |
|
|
234
|
+
| `x = x .. s` | `x ..= s` |
|
|
235
|
+
|
|
236
|
+
Only currently only applies to simple identifiers.
|
|
237
|
+
|
|
238
|
+
### `floorMultiply`
|
|
239
|
+
|
|
240
|
+
Reorders `Math.floor((a / b) * c)` to use the floor division operator, avoiding a `math.floor` call.
|
|
241
|
+
|
|
242
|
+
| TypeScript | Lua output |
|
|
243
|
+
| ---------------------------------- | --------------------- |
|
|
244
|
+
| `Math.floor((used / limit) * 100)` | `used * 100 // limit` |
|
|
245
|
+
|
|
246
|
+
Plain `Math.floor(a / b)` is **always** optimized to `a // b` regardless of this flag.
|
|
247
|
+
|
|
248
|
+
### `indexOf`
|
|
249
|
+
|
|
250
|
+
Emits bare `string.find` / `table.find` for `indexOf` _presence checks_ instead of the full `(find or 0) - 1` pattern.
|
|
251
|
+
|
|
252
|
+
| TypeScript | Lua output |
|
|
253
|
+
| ----------------------- | -------------------------------- |
|
|
254
|
+
| `s.indexOf(x) >= 0` | `string.find(s, x, 1, true)` |
|
|
255
|
+
| `s.indexOf(x) !== -1` | `string.find(s, x, 1, true)` |
|
|
256
|
+
| `s.indexOf(x) === -1` | `not string.find(s, x, 1, true)` |
|
|
257
|
+
| `arr.indexOf(x) >= 0` | `table.find(arr, x)` |
|
|
258
|
+
| `arr.indexOf(x) === -1` | `not table.find(arr, x)` |
|
|
259
|
+
|
|
260
|
+
Bare `indexOf` calls without a comparison will still emit `(find or 0) - 1` to retain 0-index style responses.
|
|
261
|
+
|
|
262
|
+
### `shortenTemps`
|
|
263
|
+
|
|
264
|
+
Shortens TSTL's destructuring temp names and collapses consecutive field accesses into multi-assignment.
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
const { a, b } = fn()
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Default output:
|
|
271
|
+
|
|
272
|
+
```lua
|
|
273
|
+
local ____fn_result_0 = fn()
|
|
274
|
+
local a = ____fn_result_0.a
|
|
275
|
+
local b = ____fn_result_0.b
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Optimized output:
|
|
279
|
+
|
|
280
|
+
```lua
|
|
281
|
+
local _r0 = fn()
|
|
282
|
+
local a, b = _r0.a, _r0.b
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### `inlineLocals`
|
|
286
|
+
|
|
287
|
+
Merges forward-declared `local x` with its first `x = value` assignment when there are no references to `x` in between.
|
|
288
|
+
|
|
289
|
+
Default output:
|
|
290
|
+
|
|
291
|
+
```lua
|
|
292
|
+
local x
|
|
293
|
+
x = 5
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Optimized output:
|
|
297
|
+
|
|
298
|
+
```lua
|
|
299
|
+
local x = 5
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### `numericConcat`
|
|
303
|
+
|
|
304
|
+
Strips `tostring()` from number-typed (and string-typed) template literal interpolations, since Luau's `..` operator handles numeric concatenation natively.
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
// count is number
|
|
308
|
+
const msg = `items: ${count}`
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Default output:
|
|
312
|
+
|
|
313
|
+
```lua
|
|
314
|
+
local msg = "items: " .. tostring(count)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Optimized output:
|
|
318
|
+
|
|
319
|
+
```lua
|
|
320
|
+
local msg = "items: " .. count
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Non-numeric types (booleans, `any`, etc.) still get wrapped in `tostring()`.
|
|
324
|
+
|
|
325
|
+
### `defaultParams`
|
|
326
|
+
|
|
327
|
+
Collapses default-parameter nil-checks into a single `or` expression.
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
function respondPoll(extraHtml = "") {
|
|
331
|
+
// ...
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Default output:
|
|
336
|
+
|
|
337
|
+
```lua
|
|
338
|
+
function respondPoll(extraHtml)
|
|
339
|
+
if extraHtml == nil then
|
|
340
|
+
extraHtml = ""
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Optimized output:
|
|
346
|
+
|
|
347
|
+
```lua
|
|
348
|
+
function respondPoll(extraHtml)
|
|
349
|
+
extraHtml = extraHtml or ""
|
|
350
|
+
end
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Safe for string and number defaults (both truthy in Lua). Not applied to `false` defaults.
|
|
354
|
+
|
|
137
355
|
## Keeping output small
|
|
138
356
|
|
|
139
357
|
Some TypeScript patterns pull in large TSTL runtime helpers. Here are recommendations for keeping output lean:
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
/**
|
|
3
|
+
* PascalCase class names that map to lowercase Lua globals.
|
|
4
|
+
* `new Vector(...)` is handled by @customConstructor, but static access
|
|
5
|
+
* like `Vector.zero` emits `Vector.zero` in Lua -- which doesn't exist.
|
|
6
|
+
* The PropertyAccessExpression visitor rewrites the Lua identifier to lowercase.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PASCAL_TO_LOWER: Record<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* TSTL treats "bit32" as a Lua keyword and renames it to "____bit32" in output.
|
|
11
|
+
* This is incorrect for Luau where bit32 is a valid global library.
|
|
12
|
+
* The visitor rewrites the mangled name back; the diagnostic is suppressed
|
|
13
|
+
* separately in consumers (e.g. the playground transpiler worker).
|
|
14
|
+
*/
|
|
15
|
+
export declare const TSTL_KEYWORD_FIXUPS: Record<string, string>;
|
|
16
|
+
export declare const BINARY_BITWISE_OPS: Record<number, string>;
|
|
17
|
+
/**
|
|
18
|
+
* Compound bitwise assignment tokens (`&=`, `|=`, etc.) map to the same
|
|
19
|
+
* `bit32.*` functions as their non-compound counterparts. We handle
|
|
20
|
+
* these at the TypeScript AST level rather than patching the Lua AST,
|
|
21
|
+
* because TSTL's desugaring loses the distinction between `>>=`
|
|
22
|
+
* (arshift) and `>>>=` (rshift) -- both lower to the same Lua operator.
|
|
23
|
+
*/
|
|
24
|
+
export declare const COMPOUND_BITWISE_OPS: Record<number, string>;
|
|
25
|
+
export declare const EQUALITY_OPS: Set<ts.SyntaxKind>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
/**
|
|
3
|
+
* PascalCase class names that map to lowercase Lua globals.
|
|
4
|
+
* `new Vector(...)` is handled by @customConstructor, but static access
|
|
5
|
+
* like `Vector.zero` emits `Vector.zero` in Lua -- which doesn't exist.
|
|
6
|
+
* The PropertyAccessExpression visitor rewrites the Lua identifier to lowercase.
|
|
7
|
+
*/
|
|
8
|
+
export const PASCAL_TO_LOWER = {
|
|
9
|
+
Vector: "vector",
|
|
10
|
+
Quaternion: "quaternion",
|
|
11
|
+
UUID: "uuid",
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* TSTL treats "bit32" as a Lua keyword and renames it to "____bit32" in output.
|
|
15
|
+
* This is incorrect for Luau where bit32 is a valid global library.
|
|
16
|
+
* The visitor rewrites the mangled name back; the diagnostic is suppressed
|
|
17
|
+
* separately in consumers (e.g. the playground transpiler worker).
|
|
18
|
+
*/
|
|
19
|
+
export const TSTL_KEYWORD_FIXUPS = {
|
|
20
|
+
____bit32: "bit32",
|
|
21
|
+
};
|
|
22
|
+
export const BINARY_BITWISE_OPS = {
|
|
23
|
+
[ts.SyntaxKind.AmpersandToken]: "band",
|
|
24
|
+
[ts.SyntaxKind.BarToken]: "bor",
|
|
25
|
+
[ts.SyntaxKind.CaretToken]: "bxor",
|
|
26
|
+
[ts.SyntaxKind.LessThanLessThanToken]: "lshift",
|
|
27
|
+
[ts.SyntaxKind.GreaterThanGreaterThanToken]: "arshift",
|
|
28
|
+
[ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken]: "rshift",
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Compound bitwise assignment tokens (`&=`, `|=`, etc.) map to the same
|
|
32
|
+
* `bit32.*` functions as their non-compound counterparts. We handle
|
|
33
|
+
* these at the TypeScript AST level rather than patching the Lua AST,
|
|
34
|
+
* because TSTL's desugaring loses the distinction between `>>=`
|
|
35
|
+
* (arshift) and `>>>=` (rshift) -- both lower to the same Lua operator.
|
|
36
|
+
*/
|
|
37
|
+
export const COMPOUND_BITWISE_OPS = {
|
|
38
|
+
[ts.SyntaxKind.AmpersandEqualsToken]: "band",
|
|
39
|
+
[ts.SyntaxKind.BarEqualsToken]: "bor",
|
|
40
|
+
[ts.SyntaxKind.CaretEqualsToken]: "bxor",
|
|
41
|
+
[ts.SyntaxKind.LessThanLessThanEqualsToken]: "lshift",
|
|
42
|
+
[ts.SyntaxKind.GreaterThanGreaterThanEqualsToken]: "arshift",
|
|
43
|
+
[ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken]: "rshift",
|
|
44
|
+
};
|
|
45
|
+
export const EQUALITY_OPS = new Set([
|
|
46
|
+
ts.SyntaxKind.EqualsEqualsToken,
|
|
47
|
+
ts.SyntaxKind.EqualsEqualsEqualsToken,
|
|
48
|
+
ts.SyntaxKind.ExclamationEqualsToken,
|
|
49
|
+
ts.SyntaxKind.ExclamationEqualsEqualsToken,
|
|
50
|
+
]);
|
package/dist/define.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
export type DefineMap = Map<string, boolean | number | string>;
|
|
3
|
+
/**
|
|
4
|
+
* Attempt to statically evaluate a TS expression against the define map.
|
|
5
|
+
* Returns the resolved value, or `undefined` if the expression cannot be
|
|
6
|
+
* statically resolved (i.e. it should be left to the normal transpiler).
|
|
7
|
+
*
|
|
8
|
+
* Supported forms:
|
|
9
|
+
* - Bare identifier: `CONFIG_X` -> lookup
|
|
10
|
+
* - Negation: `!CONFIG_X`
|
|
11
|
+
* - Strict equality: `CONFIG_X === true`, `CONFIG_X !== false`, etc.
|
|
12
|
+
*/
|
|
13
|
+
export declare function tryEvaluateCondition(expr: ts.Expression, defineMap: DefineMap): boolean | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Check whether a node has a `@define FLAG` JSDoc tag whose flag
|
|
16
|
+
* is in the define map and resolves to falsy. When true, the
|
|
17
|
+
* entire declaration should be stripped from the output.
|
|
18
|
+
*/
|
|
19
|
+
export declare function shouldStripDefineGuard(node: ts.Node, defineMap: DefineMap): boolean;
|
package/dist/define.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
/**
|
|
3
|
+
* Attempt to statically evaluate a TS expression against the define map.
|
|
4
|
+
* Returns the resolved value, or `undefined` if the expression cannot be
|
|
5
|
+
* statically resolved (i.e. it should be left to the normal transpiler).
|
|
6
|
+
*
|
|
7
|
+
* Supported forms:
|
|
8
|
+
* - Bare identifier: `CONFIG_X` -> lookup
|
|
9
|
+
* - Negation: `!CONFIG_X`
|
|
10
|
+
* - Strict equality: `CONFIG_X === true`, `CONFIG_X !== false`, etc.
|
|
11
|
+
*/
|
|
12
|
+
export function tryEvaluateCondition(expr, defineMap) {
|
|
13
|
+
// Bare identifier: CONFIG_X
|
|
14
|
+
if (ts.isIdentifier(expr)) {
|
|
15
|
+
const value = defineMap.get(expr.text);
|
|
16
|
+
return value === undefined ? undefined : !!value;
|
|
17
|
+
}
|
|
18
|
+
// Negation: !CONFIG_X
|
|
19
|
+
if (ts.isPrefixUnaryExpression(expr) && expr.operator === ts.SyntaxKind.ExclamationToken) {
|
|
20
|
+
const inner = tryEvaluateCondition(expr.operand, defineMap);
|
|
21
|
+
return inner === undefined ? undefined : !inner;
|
|
22
|
+
}
|
|
23
|
+
// Strict equality/inequality: CONFIG_X === true, CONFIG_X !== false, etc.
|
|
24
|
+
if (ts.isBinaryExpression(expr)) {
|
|
25
|
+
const op = expr.operatorToken.kind;
|
|
26
|
+
if (op !== ts.SyntaxKind.EqualsEqualsEqualsToken &&
|
|
27
|
+
op !== ts.SyntaxKind.ExclamationEqualsEqualsToken) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
let identValue;
|
|
31
|
+
let literalValue;
|
|
32
|
+
// Try both orientations: CONFIG_X === true or true === CONFIG_X
|
|
33
|
+
if (ts.isIdentifier(expr.left)) {
|
|
34
|
+
identValue = defineMap.get(expr.left.text);
|
|
35
|
+
literalValue = extractLiteral(expr.right);
|
|
36
|
+
}
|
|
37
|
+
else if (ts.isIdentifier(expr.right)) {
|
|
38
|
+
identValue = defineMap.get(expr.right.text);
|
|
39
|
+
literalValue = extractLiteral(expr.left);
|
|
40
|
+
}
|
|
41
|
+
if (identValue === undefined || literalValue === undefined)
|
|
42
|
+
return undefined;
|
|
43
|
+
const equal = identValue === literalValue;
|
|
44
|
+
return op === ts.SyntaxKind.EqualsEqualsEqualsToken ? equal : !equal;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check whether a node has a `@define FLAG` JSDoc tag whose flag
|
|
50
|
+
* is in the define map and resolves to falsy. When true, the
|
|
51
|
+
* entire declaration should be stripped from the output.
|
|
52
|
+
*/
|
|
53
|
+
export function shouldStripDefineGuard(node, defineMap) {
|
|
54
|
+
const jsDocs = node.jsDoc;
|
|
55
|
+
if (!jsDocs || jsDocs.length === 0)
|
|
56
|
+
return false;
|
|
57
|
+
for (const tag of ts.getJSDocTags(node)) {
|
|
58
|
+
if (tag.tagName.text === "define" && typeof tag.comment === "string") {
|
|
59
|
+
const flag = tag.comment.trim();
|
|
60
|
+
if (defineMap.has(flag) && !defineMap.get(flag)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function extractLiteral(expr) {
|
|
68
|
+
if (expr.kind === ts.SyntaxKind.TrueKeyword)
|
|
69
|
+
return true;
|
|
70
|
+
if (expr.kind === ts.SyntaxKind.FalseKeyword)
|
|
71
|
+
return false;
|
|
72
|
+
if (ts.isNumericLiteral(expr))
|
|
73
|
+
return Number(expr.text);
|
|
74
|
+
if (ts.isStringLiteral(expr))
|
|
75
|
+
return expr.text;
|
|
76
|
+
// Handle negative numbers: -123
|
|
77
|
+
if (ts.isPrefixUnaryExpression(expr) &&
|
|
78
|
+
expr.operator === ts.SyntaxKind.MinusToken &&
|
|
79
|
+
ts.isNumericLiteral(expr.operand)) {
|
|
80
|
+
return -Number(expr.operand.text);
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
1
|
import * as tstl from "typescript-to-lua";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import type { CallTransform } from "./transforms.js";
|
|
3
|
+
import type { OptimizeFlags } from "./optimize.js";
|
|
4
|
+
import type { DefineMap } from "./define.js";
|
|
5
|
+
export type { CallTransform, OptimizeFlags, DefineMap };
|
|
6
|
+
export interface SluaPluginOptions {
|
|
7
|
+
/** Enable per-transform output optimizations. Pass `true` to enable all. */
|
|
8
|
+
optimize?: boolean | OptimizeFlags;
|
|
9
|
+
/** Compile-time defines for dead code elimination. */
|
|
10
|
+
define?: Record<string, boolean | number | string>;
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}
|
|
13
|
+
declare function createPlugin(options?: SluaPluginOptions): tstl.Plugin;
|
|
14
|
+
export default createPlugin;
|