@gwigz/slua-tstl-plugin 0.1.0 → 0.1.2
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 +14 -15
- package/dist/index.js +4 -14
- package/package.json +4 -3
- package/dist/__tests__/helpers.d.ts +0 -1
- package/dist/__tests__/helpers.js +0 -16
- package/dist/__tests__/plugin.test.d.ts +0 -1
- package/dist/__tests__/plugin.test.js +0 -314
package/README.md
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
- Translates TypeScript patterns to native Luau/LSL equivalents (see below)
|
|
8
8
|
- Handles adjusting `Vector`, `Quaternion`, and `UUID` casing
|
|
9
9
|
- Validates `luaTarget` is set to `Luau`
|
|
10
|
-
- Warns if `luaLibImport` is not `none` or `inline` (for now)
|
|
11
10
|
|
|
12
11
|
## Transforms
|
|
13
12
|
|
|
@@ -33,20 +32,20 @@ For SL-typed JSON (preserving vector/quaternion/uuid), use `lljson.slencode`/`ll
|
|
|
33
32
|
|
|
34
33
|
String methods are translated to LSL `ll.*` functions or Luau `string.*` stdlib calls:
|
|
35
34
|
|
|
36
|
-
| TypeScript | Lua output
|
|
37
|
-
| ---------------------- |
|
|
38
|
-
| `str.toUpperCase()` | `ll.ToUpper(str)`
|
|
39
|
-
| `str.toLowerCase()` | `ll.ToLower(str)`
|
|
40
|
-
| `str.trim()` | `ll.StringTrim(str, STRING_TRIM)`
|
|
41
|
-
| `str.trimStart()` | `ll.StringTrim(str, STRING_TRIM_HEAD)`
|
|
42
|
-
| `str.trimEnd()` | `ll.StringTrim(str, STRING_TRIM_TAIL)`
|
|
43
|
-
| `str.indexOf(x)` | `
|
|
44
|
-
| `str.includes(x)` | `string.find(str, x, 1, true) ~= nil`
|
|
45
|
-
| `str.startsWith(x)` | `string.find(str, x, 1, true) == 1`
|
|
46
|
-
| `str.split(sep)` | `string.split(str, sep)`
|
|
47
|
-
| `str.repeat(n)` | `string.rep(str, n)`
|
|
48
|
-
| `str.substring(start)` | `string.sub(str, start + 1)`
|
|
49
|
-
| `str.substring(s, e)` | `string.sub(str, s + 1, e)`
|
|
35
|
+
| TypeScript | Lua output |
|
|
36
|
+
| ---------------------- | ----------------------------------------- |
|
|
37
|
+
| `str.toUpperCase()` | `ll.ToUpper(str)` |
|
|
38
|
+
| `str.toLowerCase()` | `ll.ToLower(str)` |
|
|
39
|
+
| `str.trim()` | `ll.StringTrim(str, STRING_TRIM)` |
|
|
40
|
+
| `str.trimStart()` | `ll.StringTrim(str, STRING_TRIM_HEAD)` |
|
|
41
|
+
| `str.trimEnd()` | `ll.StringTrim(str, STRING_TRIM_TAIL)` |
|
|
42
|
+
| `str.indexOf(x)` | `(string.find(str, x, 1, true) or 0) - 1` |
|
|
43
|
+
| `str.includes(x)` | `string.find(str, x, 1, true) ~= nil` |
|
|
44
|
+
| `str.startsWith(x)` | `string.find(str, x, 1, true) == 1` |
|
|
45
|
+
| `str.split(sep)` | `string.split(str, sep)` |
|
|
46
|
+
| `str.repeat(n)` | `string.rep(str, n)` |
|
|
47
|
+
| `str.substring(start)` | `string.sub(str, start + 1)` |
|
|
48
|
+
| `str.substring(s, e)` | `string.sub(str, s + 1, e)` |
|
|
50
49
|
|
|
51
50
|
> [!NOTE]
|
|
52
51
|
> `str.indexOf(x, fromIndex)` and `str.startsWith(x, position)` with a second argument fall through to TSTL's default handling. Similarly, `str.split()` with no separator is not transformed.
|
package/dist/index.js
CHANGED
|
@@ -222,13 +222,15 @@ const CALL_TRANSFORMS = [
|
|
|
222
222
|
return createNamespacedCall("ll", "StringTrim", [str, tstl.createIdentifier("STRING_TRIM_TAIL")], node);
|
|
223
223
|
},
|
|
224
224
|
},
|
|
225
|
-
// str.indexOf(x) ->
|
|
225
|
+
// str.indexOf(x) -> (string.find(str, x, 1, true) or 0) - 1
|
|
226
226
|
{
|
|
227
227
|
match: (node, checker) => isMethodCall(node, checker, isStringType, "indexOf", 1),
|
|
228
228
|
emit: (node, context) => {
|
|
229
229
|
const str = context.transformExpression(node.expression.expression);
|
|
230
230
|
const search = context.transformExpression(node.arguments[0]);
|
|
231
|
-
|
|
231
|
+
const findCall = createNamespacedCall("string", "find", [str, search, tstl.createNumericLiteral(1), tstl.createBooleanLiteral(true)], node);
|
|
232
|
+
const findOrZero = tstl.createBinaryExpression(findCall, tstl.createNumericLiteral(0), tstl.SyntaxKind.OrOperator, node);
|
|
233
|
+
return tstl.createBinaryExpression(tstl.createParenthesizedExpression(findOrZero), tstl.createNumericLiteral(1), tstl.SyntaxKind.SubtractionOperator, node);
|
|
232
234
|
},
|
|
233
235
|
},
|
|
234
236
|
// str.indexOf(x, fromIndex) -> (string.find(str, x, fromIndex + 1, true) or 0) - 1
|
|
@@ -430,18 +432,6 @@ const plugin = {
|
|
|
430
432
|
source: "@gwigz/slua-tstl-plugin",
|
|
431
433
|
});
|
|
432
434
|
}
|
|
433
|
-
if (options.luaLibImport !== undefined &&
|
|
434
|
-
![tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Inline].includes(options.luaLibImport)) {
|
|
435
|
-
diagnostics.push({
|
|
436
|
-
file: undefined,
|
|
437
|
-
start: undefined,
|
|
438
|
-
length: undefined,
|
|
439
|
-
messageText: '@gwigz/slua-tstl-plugin requires luaLibImport to be "none" or "inline"',
|
|
440
|
-
category: ts.DiagnosticCategory.Warning,
|
|
441
|
-
code: 90002,
|
|
442
|
-
source: "@gwigz/slua-tstl-plugin",
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
435
|
return diagnostics;
|
|
446
436
|
},
|
|
447
437
|
beforeEmit(program, _options, _emitHost, result) {
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gwigz/slua-tstl-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "TypeScriptToLua plugin for targeting Second Life's SLua runtime",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/gwigz/slua.git",
|
|
8
|
+
"url": "git+https://github.com/gwigz/slua.git",
|
|
9
9
|
"directory": "packages/tstl-plugin"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
|
-
"dist"
|
|
12
|
+
"dist",
|
|
13
|
+
"!dist/__tests__"
|
|
13
14
|
],
|
|
14
15
|
"main": "dist/index.js",
|
|
15
16
|
"types": "dist/index.d.ts",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function transpile(code: string): string;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import * as tstl from "typescript-to-lua";
|
|
2
|
-
import plugin from "../index";
|
|
3
|
-
export function transpile(code) {
|
|
4
|
-
const result = tstl.transpileVirtualProject({ "main.ts": code }, {
|
|
5
|
-
luaTarget: tstl.LuaTarget.Luau,
|
|
6
|
-
noImplicitSelf: true,
|
|
7
|
-
noHeader: true,
|
|
8
|
-
luaLibImport: tstl.LuaLibImportKind.None,
|
|
9
|
-
luaPlugins: [{ plugin: plugin }],
|
|
10
|
-
});
|
|
11
|
-
if (result.diagnostics.length > 0) {
|
|
12
|
-
const messages = result.diagnostics.map((d) => typeof d.messageText === "string" ? d.messageText : d.messageText.messageText);
|
|
13
|
-
throw new Error(`Transpilation failed:\n${messages.join("\n")}`);
|
|
14
|
-
}
|
|
15
|
-
return result.transpiledFiles.find((f) => f.outPath === "main.lua")?.lua ?? "";
|
|
16
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { readFileSync } from "fs";
|
|
3
|
-
import { resolve } from "path";
|
|
4
|
-
import * as ts from "typescript";
|
|
5
|
-
import * as tstl from "typescript-to-lua";
|
|
6
|
-
import plugin from "../index";
|
|
7
|
-
import { transpile as transpileSimple } from "./helpers";
|
|
8
|
-
const TYPES_PATH = resolve(import.meta.dir, "../../../../packages/types/index.d.ts");
|
|
9
|
-
const LANG_EXT_PATH = resolve(import.meta.dir, "../../../../node_modules/@typescript-to-lua/language-extensions/index.d.ts");
|
|
10
|
-
const sluaTypes = readFileSync(TYPES_PATH, "utf-8");
|
|
11
|
-
const langExt = readFileSync(LANG_EXT_PATH, "utf-8");
|
|
12
|
-
function transpile(code) {
|
|
13
|
-
const result = tstl.transpileVirtualProject({
|
|
14
|
-
"main.ts": code,
|
|
15
|
-
"language-extensions.d.ts": langExt,
|
|
16
|
-
"slua.d.ts": sluaTypes,
|
|
17
|
-
}, {
|
|
18
|
-
luaTarget: tstl.LuaTarget.Luau,
|
|
19
|
-
noImplicitSelf: true,
|
|
20
|
-
noHeader: true,
|
|
21
|
-
luaLibImport: tstl.LuaLibImportKind.Inline,
|
|
22
|
-
noImplicitGlobalVariables: true,
|
|
23
|
-
noLib: true,
|
|
24
|
-
strict: true,
|
|
25
|
-
luaPlugins: [{ plugin: plugin }],
|
|
26
|
-
});
|
|
27
|
-
return result.transpiledFiles.find((f) => f.outPath === "main.lua")?.lua ?? "";
|
|
28
|
-
}
|
|
29
|
-
describe("ts-slua plugin", () => {
|
|
30
|
-
it("beforeTransform errors on non-Luau target", () => {
|
|
31
|
-
const diagnostics = plugin.beforeTransform({}, { luaTarget: tstl.LuaTarget.Lua53 }, {});
|
|
32
|
-
expect(diagnostics).toHaveLength(1);
|
|
33
|
-
expect(diagnostics[0].messageText).toContain("Luau");
|
|
34
|
-
});
|
|
35
|
-
it("beforeTransform passes with correct config", () => {
|
|
36
|
-
const diagnostics = plugin.beforeTransform({}, {
|
|
37
|
-
luaTarget: tstl.LuaTarget.Luau,
|
|
38
|
-
luaLibImport: tstl.LuaLibImportKind.None,
|
|
39
|
-
}, {});
|
|
40
|
-
expect(diagnostics).toHaveLength(0);
|
|
41
|
-
});
|
|
42
|
-
it("beforeTransform warns on non-none luaLibImport", () => {
|
|
43
|
-
const diagnostics = plugin.beforeTransform({}, {
|
|
44
|
-
luaTarget: tstl.LuaTarget.Luau,
|
|
45
|
-
luaLibImport: tstl.LuaLibImportKind.Require,
|
|
46
|
-
}, {});
|
|
47
|
-
expect(diagnostics).toHaveLength(1);
|
|
48
|
-
expect(diagnostics[0].category).toBe(ts.DiagnosticCategory.Warning);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
describe("transpilation output", () => {
|
|
52
|
-
it("does not inject self into LLEvents callbacks", () => {
|
|
53
|
-
const lua = transpile(`
|
|
54
|
-
LLEvents.on("touch_start", function(events) {
|
|
55
|
-
ll.Say(0, "touched")
|
|
56
|
-
})
|
|
57
|
-
`);
|
|
58
|
-
expect(lua).toContain("LLEvents:on");
|
|
59
|
-
expect(lua).not.toMatch(/function\(self/);
|
|
60
|
-
});
|
|
61
|
-
it("does not inject self into LLTimers callbacks", () => {
|
|
62
|
-
const lua = transpile(`
|
|
63
|
-
LLTimers.every(10, function(scheduled, interval) {
|
|
64
|
-
ll.Say(0, "tick")
|
|
65
|
-
})
|
|
66
|
-
`);
|
|
67
|
-
expect(lua).toContain("LLTimers:every");
|
|
68
|
-
expect(lua).not.toMatch(/function\(self/);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
describe("floor division", () => {
|
|
72
|
-
it("translates Math.floor(a / b) to floor division operator", () => {
|
|
73
|
-
const lua = transpileSimple("declare const a: number, b: number;\nconst x = Math.floor(a / b)");
|
|
74
|
-
expect(lua).toContain("a // b");
|
|
75
|
-
expect(lua).not.toContain("math.floor");
|
|
76
|
-
});
|
|
77
|
-
it("handles literal operands", () => {
|
|
78
|
-
const lua = transpileSimple("const x = Math.floor(10 / 3)");
|
|
79
|
-
expect(lua).toContain("10 // 3");
|
|
80
|
-
});
|
|
81
|
-
it("does not transform Math.floor with non-division argument", () => {
|
|
82
|
-
const lua = transpileSimple("declare const a: number;\nconst x = Math.floor(a)");
|
|
83
|
-
expect(lua).not.toContain("//");
|
|
84
|
-
expect(lua).toContain("math.floor(a)");
|
|
85
|
-
});
|
|
86
|
-
it("does not transform Math.floor with complex non-division expression", () => {
|
|
87
|
-
const lua = transpileSimple("declare const a: number, b: number;\nconst x = Math.floor(a + b)");
|
|
88
|
-
expect(lua).not.toContain("//");
|
|
89
|
-
expect(lua).toContain("math.floor");
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
describe("bitwise operators", () => {
|
|
93
|
-
it("translates & to bit32.band", () => {
|
|
94
|
-
const lua = transpileSimple("const x = (1 as number) & (2 as number)");
|
|
95
|
-
expect(lua).toContain("bit32.band(1, 2)");
|
|
96
|
-
});
|
|
97
|
-
it("translates | to bit32.bor", () => {
|
|
98
|
-
const lua = transpileSimple("const x = (1 as number) | (2 as number)");
|
|
99
|
-
expect(lua).toContain("bit32.bor(1, 2)");
|
|
100
|
-
});
|
|
101
|
-
it("translates ^ to bit32.bxor", () => {
|
|
102
|
-
const lua = transpileSimple("const x = (1 as number) ^ (2 as number)");
|
|
103
|
-
expect(lua).toContain("bit32.bxor(1, 2)");
|
|
104
|
-
});
|
|
105
|
-
it("translates << to bit32.lshift", () => {
|
|
106
|
-
const lua = transpileSimple("const x = (1 as number) << (2 as number)");
|
|
107
|
-
expect(lua).toContain("bit32.lshift(1, 2)");
|
|
108
|
-
});
|
|
109
|
-
it("translates >> to bit32.arshift", () => {
|
|
110
|
-
const lua = transpileSimple("const x = (1 as number) >> (2 as number)");
|
|
111
|
-
expect(lua).toContain("bit32.arshift(1, 2)");
|
|
112
|
-
});
|
|
113
|
-
it("translates >>> to bit32.rshift", () => {
|
|
114
|
-
const lua = transpileSimple("const x = (1 as number) >>> (2 as number)");
|
|
115
|
-
expect(lua).toContain("bit32.rshift(1, 2)");
|
|
116
|
-
});
|
|
117
|
-
it("translates ~ to bit32.bnot", () => {
|
|
118
|
-
const lua = transpileSimple("const x = ~(1 as number)");
|
|
119
|
-
expect(lua).toContain("bit32.bnot(1)");
|
|
120
|
-
});
|
|
121
|
-
describe("btest optimization", () => {
|
|
122
|
-
it("translates (a & b) !== 0 to bit32.btest", () => {
|
|
123
|
-
const lua = transpileSimple("declare const a: number, b: number;\nconst x = (a & b) !== 0");
|
|
124
|
-
expect(lua).toContain("bit32.btest(a, b)");
|
|
125
|
-
expect(lua).not.toContain("bit32.band");
|
|
126
|
-
});
|
|
127
|
-
it("translates (a & b) != 0 to bit32.btest", () => {
|
|
128
|
-
const lua = transpileSimple("declare const a: number, b: number;\nconst x = (a & b) != 0");
|
|
129
|
-
expect(lua).toContain("bit32.btest(a, b)");
|
|
130
|
-
});
|
|
131
|
-
it("translates (a & b) === 0 to not bit32.btest", () => {
|
|
132
|
-
const lua = transpileSimple("declare const a: number, b: number;\nconst x = (a & b) === 0");
|
|
133
|
-
expect(lua).toContain("not bit32.btest(a, b)");
|
|
134
|
-
});
|
|
135
|
-
it("translates (a & b) == 0 to not bit32.btest", () => {
|
|
136
|
-
const lua = transpileSimple("declare const a: number, b: number;\nconst x = (a & b) == 0");
|
|
137
|
-
expect(lua).toContain("not bit32.btest(a, b)");
|
|
138
|
-
});
|
|
139
|
-
it("translates 0 !== (a & b) to bit32.btest (flipped)", () => {
|
|
140
|
-
const lua = transpileSimple("declare const a: number, b: number;\nconst x = 0 !== (a & b)");
|
|
141
|
-
expect(lua).toContain("bit32.btest(a, b)");
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
it("handles compound expressions: (a & b) | c", () => {
|
|
145
|
-
const lua = transpileSimple("declare const a: number, b: number, c: number;\nconst x = (a & b) | c");
|
|
146
|
-
expect(lua).toContain("bit32.bor(");
|
|
147
|
-
expect(lua).toContain("bit32.band(a, b)");
|
|
148
|
-
expect(lua).toMatch(/bit32\.bor\(\s*bit32\.band\(a, b\)/);
|
|
149
|
-
});
|
|
150
|
-
describe("compound bitwise assignments", () => {
|
|
151
|
-
it("translates &= to bit32.band", () => {
|
|
152
|
-
const lua = transpileSimple("declare let a: number; a &= 3");
|
|
153
|
-
expect(lua).toContain("bit32.band(a, 3)");
|
|
154
|
-
});
|
|
155
|
-
it("translates |= to bit32.bor", () => {
|
|
156
|
-
const lua = transpileSimple("declare let a: number; a |= 3");
|
|
157
|
-
expect(lua).toContain("bit32.bor(a, 3)");
|
|
158
|
-
});
|
|
159
|
-
it("translates ^= to bit32.bxor", () => {
|
|
160
|
-
const lua = transpileSimple("declare let a: number; a ^= 3");
|
|
161
|
-
expect(lua).toContain("bit32.bxor(a, 3)");
|
|
162
|
-
});
|
|
163
|
-
it("translates <<= to bit32.lshift", () => {
|
|
164
|
-
const lua = transpileSimple("declare let a: number; a <<= 3");
|
|
165
|
-
expect(lua).toContain("bit32.lshift(a, 3)");
|
|
166
|
-
});
|
|
167
|
-
it("translates >>= to bit32.arshift", () => {
|
|
168
|
-
const lua = transpileSimple("declare let a: number; a >>= 3");
|
|
169
|
-
expect(lua).toContain("bit32.arshift(a, 3)");
|
|
170
|
-
});
|
|
171
|
-
it("translates >>>= to bit32.rshift", () => {
|
|
172
|
-
const lua = transpileSimple("declare let a: number; a >>>= 3");
|
|
173
|
-
expect(lua).toContain("bit32.rshift(a, 3)");
|
|
174
|
-
});
|
|
175
|
-
it("handles property access LHS", () => {
|
|
176
|
-
const lua = transpileSimple("declare let obj: {prop: number}; obj.prop &= 3");
|
|
177
|
-
expect(lua).toContain("bit32.band(");
|
|
178
|
-
expect(lua).not.toMatch(/\s&\s/);
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
it("does not affect non-bitwise binary operators", () => {
|
|
182
|
-
const lua = transpileSimple("const x = 1 + 2");
|
|
183
|
-
expect(lua).toContain("1 + 2");
|
|
184
|
-
expect(lua).not.toContain("bit32");
|
|
185
|
-
});
|
|
186
|
-
it("does not affect non-bitwise unary operators", () => {
|
|
187
|
-
const lua = transpileSimple("const x = -(1 as number)");
|
|
188
|
-
expect(lua).toContain("-1");
|
|
189
|
-
expect(lua).not.toContain("bit32");
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
describe("JSON transforms", () => {
|
|
193
|
-
it("translates JSON.stringify to lljson.encode", () => {
|
|
194
|
-
const lua = transpileSimple("declare const obj: any;\nconst s = JSON.stringify(obj)");
|
|
195
|
-
expect(lua).toContain("lljson.encode(obj)");
|
|
196
|
-
expect(lua).not.toContain("JSON");
|
|
197
|
-
});
|
|
198
|
-
it("translates JSON.parse to lljson.decode", () => {
|
|
199
|
-
const lua = transpileSimple('declare const s: string;\nconst obj = JSON.parse(s)');
|
|
200
|
-
expect(lua).toContain("lljson.decode(s)");
|
|
201
|
-
expect(lua).not.toContain("JSON");
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
describe("base64 transforms", () => {
|
|
205
|
-
it("translates btoa to llbase64.encode", () => {
|
|
206
|
-
const lua = transpileSimple("declare const s: string;\nconst b = btoa(s)");
|
|
207
|
-
expect(lua).toContain("llbase64.encode(s)");
|
|
208
|
-
expect(lua).not.toContain("btoa");
|
|
209
|
-
});
|
|
210
|
-
it("translates atob to llbase64.decode", () => {
|
|
211
|
-
const lua = transpileSimple("declare const b: string;\nconst s = atob(b)");
|
|
212
|
-
expect(lua).toContain("llbase64.decode(b)");
|
|
213
|
-
expect(lua).not.toContain("atob");
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
describe("string ll.* transforms", () => {
|
|
217
|
-
it("translates toUpperCase to ll.ToUpper", () => {
|
|
218
|
-
const lua = transpileSimple('const s = "hello".toUpperCase()');
|
|
219
|
-
expect(lua).toContain("ll.ToUpper");
|
|
220
|
-
});
|
|
221
|
-
it("translates toLowerCase to ll.ToLower", () => {
|
|
222
|
-
const lua = transpileSimple('const s = "HELLO".toLowerCase()');
|
|
223
|
-
expect(lua).toContain("ll.ToLower");
|
|
224
|
-
});
|
|
225
|
-
it("translates trim to ll.StringTrim with STRING_TRIM", () => {
|
|
226
|
-
const lua = transpileSimple('declare const s: string;\nconst x = s.trim()');
|
|
227
|
-
expect(lua).toContain("ll.StringTrim");
|
|
228
|
-
expect(lua).toContain("STRING_TRIM");
|
|
229
|
-
});
|
|
230
|
-
it("translates trimStart to ll.StringTrim with STRING_TRIM_HEAD", () => {
|
|
231
|
-
const lua = transpileSimple('interface String { trimStart(): string; }\ndeclare const s: string;\nconst x = s.trimStart()');
|
|
232
|
-
expect(lua).toContain("ll.StringTrim");
|
|
233
|
-
expect(lua).toContain("STRING_TRIM_HEAD");
|
|
234
|
-
});
|
|
235
|
-
it("translates trimEnd to ll.StringTrim with STRING_TRIM_TAIL", () => {
|
|
236
|
-
const lua = transpileSimple('interface String { trimEnd(): string; }\ndeclare const s: string;\nconst x = s.trimEnd()');
|
|
237
|
-
expect(lua).toContain("ll.StringTrim");
|
|
238
|
-
expect(lua).toContain("STRING_TRIM_TAIL");
|
|
239
|
-
});
|
|
240
|
-
it("translates indexOf (1-arg) to ll.SubStringIndex", () => {
|
|
241
|
-
const lua = transpileSimple('declare const s: string;\nconst i = s.indexOf("x")');
|
|
242
|
-
expect(lua).toContain("ll.SubStringIndex");
|
|
243
|
-
});
|
|
244
|
-
it("translates indexOf with literal fromIndex to string.find with constant folding", () => {
|
|
245
|
-
const lua = transpileSimple('declare const s: string;\nconst i = s.indexOf("x", 5)');
|
|
246
|
-
expect(lua).not.toContain("ll.SubStringIndex");
|
|
247
|
-
expect(lua).toContain('string.find(s, "x", 6, true)');
|
|
248
|
-
expect(lua).toContain("(string.find(");
|
|
249
|
-
expect(lua).toContain("or 0) - 1");
|
|
250
|
-
});
|
|
251
|
-
it("translates indexOf with expression fromIndex to string.find with + 1", () => {
|
|
252
|
-
const lua = transpileSimple('declare const s: string;\ndeclare const n: number;\nconst i = s.indexOf("x", n)');
|
|
253
|
-
expect(lua).toContain('string.find(s, "x", n + 1, true)');
|
|
254
|
-
expect(lua).toContain("or 0) - 1");
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
describe("string Luau stdlib transforms", () => {
|
|
258
|
-
it("translates includes to string.find", () => {
|
|
259
|
-
const lua = transpileSimple('interface String { includes(searchString: string): boolean }\ndeclare const s: string;\nconst b = s.includes("x")');
|
|
260
|
-
expect(lua).toContain("string.find(s,");
|
|
261
|
-
expect(lua).toContain("true");
|
|
262
|
-
expect(lua).toContain("~= nil");
|
|
263
|
-
});
|
|
264
|
-
it("translates split(sep) to string.split", () => {
|
|
265
|
-
const lua = transpileSimple('interface String { split(separator: string): string[] }\ndeclare const s: string;\nconst a = s.split(",")');
|
|
266
|
-
expect(lua).toContain("string.split(s,");
|
|
267
|
-
});
|
|
268
|
-
it("does not transform split() with no separator", () => {
|
|
269
|
-
const lua = transpileSimple("interface String { split(separator?: string): string[] }\ndeclare const s: string;\nconst a = s.split()");
|
|
270
|
-
expect(lua).not.toContain("string.split");
|
|
271
|
-
});
|
|
272
|
-
it("translates repeat to string.rep", () => {
|
|
273
|
-
const lua = transpileSimple("interface String { repeat(count: number): string }\ndeclare const s: string;\nconst r = s.repeat(3)");
|
|
274
|
-
expect(lua).toContain("string.rep(s, 3)");
|
|
275
|
-
});
|
|
276
|
-
it("translates startsWith to string.find == 1", () => {
|
|
277
|
-
const lua = transpileSimple('interface String { startsWith(searchString: string): boolean }\ndeclare const s: string;\nconst b = s.startsWith("pre")');
|
|
278
|
-
expect(lua).toContain("string.find(s,");
|
|
279
|
-
expect(lua).toContain("true");
|
|
280
|
-
expect(lua).toContain("== 1");
|
|
281
|
-
});
|
|
282
|
-
it("does not transform startsWith with position argument", () => {
|
|
283
|
-
const lua = transpileSimple('interface String { startsWith(searchString: string, position?: number): boolean }\ndeclare const s: string;\nconst b = s.startsWith("pre", 3)');
|
|
284
|
-
expect(lua).not.toContain("string.find");
|
|
285
|
-
});
|
|
286
|
-
it("translates substring(start) to string.sub with constant folding", () => {
|
|
287
|
-
const lua = transpileSimple("declare const s: string;\nconst x = s.substring(5)");
|
|
288
|
-
expect(lua).toContain("string.sub(s, 6)");
|
|
289
|
-
});
|
|
290
|
-
it("translates substring(start, end) to string.sub with constant folding", () => {
|
|
291
|
-
const lua = transpileSimple("declare const s: string;\nconst x = s.substring(1, 5)");
|
|
292
|
-
expect(lua).toContain("string.sub(s, 2, 5)");
|
|
293
|
-
});
|
|
294
|
-
it("translates substring with expression arg to start + 1", () => {
|
|
295
|
-
const lua = transpileSimple("declare const s: string;\ndeclare const i: number;\nconst x = s.substring(i)");
|
|
296
|
-
expect(lua).toContain("string.sub(s, i + 1)");
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
describe("array transforms", () => {
|
|
300
|
-
it("translates includes to table.find ~= nil", () => {
|
|
301
|
-
const lua = transpileSimple("interface Array<T> { includes(searchElement: T): boolean; }\ndeclare const arr: number[];\nconst b = arr.includes(3)");
|
|
302
|
-
expect(lua).toContain("table.find(arr, 3)");
|
|
303
|
-
expect(lua).toContain("~= nil");
|
|
304
|
-
});
|
|
305
|
-
it("translates indexOf (1-arg) to (table.find or 0) - 1", () => {
|
|
306
|
-
const lua = transpileSimple("interface Array<T> { indexOf(searchElement: T, fromIndex?: number): number; }\ndeclare const arr: number[];\nconst i = arr.indexOf(3)");
|
|
307
|
-
expect(lua).toContain("table.find(arr, 3)");
|
|
308
|
-
expect(lua).toContain("- 1");
|
|
309
|
-
});
|
|
310
|
-
it("does not transform indexOf with fromIndex argument", () => {
|
|
311
|
-
const lua = transpileSimple("interface Array<T> { indexOf(searchElement: T, fromIndex?: number): number; }\ndeclare const arr: number[];\nconst i = arr.indexOf(3, 1)");
|
|
312
|
-
expect(lua).not.toContain("table.find");
|
|
313
|
-
});
|
|
314
|
-
});
|