xray16 1.6.1 → 1.6.3
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 +38 -0
- package/package.json +7 -3
- package/plugins/inline_constants/plugin.d.ts +14 -0
- package/plugins/inline_constants/plugin.js +44 -0
- package/plugins/inline_constants/transformation/ast.d.ts +60 -0
- package/plugins/inline_constants/transformation/ast.js +185 -0
- package/plugins/inline_constants/transformation/constants.d.ts +14 -0
- package/plugins/inline_constants/transformation/constants.js +27 -0
- package/plugins/inline_constants/transformation/errors.d.ts +24 -0
- package/plugins/inline_constants/transformation/errors.js +16 -0
- package/plugins/inline_constants/transformation/evaluation.d.ts +79 -0
- package/plugins/inline_constants/transformation/evaluation.js +296 -0
- package/plugins/inline_constants/transformation/index.d.ts +6 -0
- package/plugins/inline_constants/transformation/index.js +22 -0
- package/plugins/inline_constants/transformation/transformers.d.ts +27 -0
- package/plugins/inline_constants/transformation/transformers.js +61 -0
- package/plugins/inline_constants/transformation/validation.d.ts +25 -0
- package/plugins/inline_constants/transformation/validation.js +86 -0
- package/xray16.d.ts +1295 -548
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getDeclaredLiteralValue = getDeclaredLiteralValue;
|
|
4
|
+
exports.toFoldedString = toFoldedString;
|
|
5
|
+
exports.toFiniteNumber = toFiniteNumber;
|
|
6
|
+
exports.foldBinaryExpression = foldBinaryExpression;
|
|
7
|
+
exports.evaluateConstantExpression = evaluateConstantExpression;
|
|
8
|
+
exports.resolveConstantSymbol = resolveConstantSymbol;
|
|
9
|
+
exports.getComputedDeclarationValue = getComputedDeclarationValue;
|
|
10
|
+
exports.tryGetInlineValue = tryGetInlineValue;
|
|
11
|
+
const ts = require("typescript");
|
|
12
|
+
const ast_1 = require("./ast");
|
|
13
|
+
const constants_1 = require("./constants");
|
|
14
|
+
/**
|
|
15
|
+
* Get literal value of symbol declared type, if it is a unit type.
|
|
16
|
+
* Uses declared type instead of flow-narrowed type so mutable narrowed values are never inlined.
|
|
17
|
+
*
|
|
18
|
+
* @param checker - Program type checker.
|
|
19
|
+
* @param symbol - Symbol to get declared literal value for.
|
|
20
|
+
* @returns Literal value or null.
|
|
21
|
+
*/
|
|
22
|
+
function getDeclaredLiteralValue(checker, symbol) {
|
|
23
|
+
const declaration = symbol.valueDeclaration;
|
|
24
|
+
if (declaration === undefined) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const type = checker.getTypeOfSymbolAtLocation(symbol, declaration);
|
|
28
|
+
if (type.isStringLiteral()) {
|
|
29
|
+
return type.value;
|
|
30
|
+
}
|
|
31
|
+
if (type.isNumberLiteral()) {
|
|
32
|
+
return type.value;
|
|
33
|
+
}
|
|
34
|
+
if ((type.flags & ts.TypeFlags.BooleanLiteral) !== 0) {
|
|
35
|
+
return checker.typeToString(type) === "true";
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Convert folded value to string for concatenation contexts.
|
|
41
|
+
* Non-integer numbers are rejected - JS 'String()' and LuaJIT 'tostring' format them differently
|
|
42
|
+
* (17 significant digits vs '%.14g'), so folding would change runtime-visible output.
|
|
43
|
+
*
|
|
44
|
+
* @param value - Folded value to convert.
|
|
45
|
+
* @returns String representation or null.
|
|
46
|
+
*/
|
|
47
|
+
function toFoldedString(value) {
|
|
48
|
+
if (typeof value === "string") {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === "number" && Number.isInteger(value) && Math.abs(value) < 1e14) {
|
|
52
|
+
return String(value);
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Guard folded numeric results against NaN / Infinity that cannot be emitted as lua literals.
|
|
58
|
+
*
|
|
59
|
+
* @param value - Folded numeric value.
|
|
60
|
+
* @returns Finite number or null.
|
|
61
|
+
*/
|
|
62
|
+
function toFiniteNumber(value) {
|
|
63
|
+
return Number.isFinite(value) ? value : null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Fold binary expression of constant operands with JS semantics.
|
|
67
|
+
* JS semantics are the correct target since tstl preserves them at runtime (e.g. '%' on negative operands).
|
|
68
|
+
*
|
|
69
|
+
* @param operator - Binary operator kind.
|
|
70
|
+
* @param left - Folded left operand.
|
|
71
|
+
* @param right - Folded right operand.
|
|
72
|
+
* @returns Folded value or null.
|
|
73
|
+
*/
|
|
74
|
+
function foldBinaryExpression(operator, left, right) {
|
|
75
|
+
if (operator === ts.SyntaxKind.PlusToken) {
|
|
76
|
+
if (typeof left === "number" && typeof right === "number") {
|
|
77
|
+
return toFiniteNumber(left + right);
|
|
78
|
+
}
|
|
79
|
+
const leftString = toFoldedString(left);
|
|
80
|
+
const rightString = toFoldedString(right);
|
|
81
|
+
return leftString !== null && rightString !== null ? leftString + rightString : null;
|
|
82
|
+
}
|
|
83
|
+
if (typeof left !== "number" || typeof right !== "number") {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
switch (operator) {
|
|
87
|
+
case ts.SyntaxKind.MinusToken:
|
|
88
|
+
return toFiniteNumber(left - right);
|
|
89
|
+
case ts.SyntaxKind.AsteriskToken:
|
|
90
|
+
return toFiniteNumber(left * right);
|
|
91
|
+
case ts.SyntaxKind.SlashToken:
|
|
92
|
+
return toFiniteNumber(left / right);
|
|
93
|
+
case ts.SyntaxKind.PercentToken:
|
|
94
|
+
return toFiniteNumber(left % right);
|
|
95
|
+
case ts.SyntaxKind.AsteriskAsteriskToken:
|
|
96
|
+
return toFiniteNumber(left ** right);
|
|
97
|
+
case ts.SyntaxKind.AmpersandToken:
|
|
98
|
+
return left & right;
|
|
99
|
+
case ts.SyntaxKind.BarToken:
|
|
100
|
+
return left | right;
|
|
101
|
+
case ts.SyntaxKind.CaretToken:
|
|
102
|
+
return left ^ right;
|
|
103
|
+
case ts.SyntaxKind.LessThanLessThanToken:
|
|
104
|
+
return left << right;
|
|
105
|
+
case ts.SyntaxKind.GreaterThanGreaterThanToken:
|
|
106
|
+
return left >> right;
|
|
107
|
+
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
|
108
|
+
return left >>> right;
|
|
109
|
+
default:
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Evaluate expression to a compile-time constant value when possible.
|
|
115
|
+
* Supports literals, unary/binary arithmetic, string concatenation, template literals,
|
|
116
|
+
* references to enum members / module-level const scalars / `as const` object properties
|
|
117
|
+
* and whitelisted namespace constants like 'math.pi'.
|
|
118
|
+
*
|
|
119
|
+
* @param checker - Program type checker.
|
|
120
|
+
* @param expression - Expression to evaluate.
|
|
121
|
+
* @param seen - Set of declarations on current resolution path, guards from circular references.
|
|
122
|
+
* @returns Folded constant value or null.
|
|
123
|
+
*/
|
|
124
|
+
function evaluateConstantExpression(checker, expression, seen) {
|
|
125
|
+
const node = (0, ast_1.unwrapInitializer)(expression);
|
|
126
|
+
if (node === null) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
if (ts.isStringLiteralLike(node)) {
|
|
130
|
+
return node.text;
|
|
131
|
+
}
|
|
132
|
+
if (ts.isNumericLiteral(node)) {
|
|
133
|
+
return Number(node.text);
|
|
134
|
+
}
|
|
135
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
if (ts.isPrefixUnaryExpression(node)) {
|
|
142
|
+
const operand = evaluateConstantExpression(checker, node.operand, seen);
|
|
143
|
+
if (operand === null) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
switch (node.operator) {
|
|
147
|
+
case ts.SyntaxKind.MinusToken:
|
|
148
|
+
return typeof operand === "number" ? toFiniteNumber(-operand) : null;
|
|
149
|
+
case ts.SyntaxKind.PlusToken:
|
|
150
|
+
return typeof operand === "number" ? operand : null;
|
|
151
|
+
case ts.SyntaxKind.TildeToken:
|
|
152
|
+
return typeof operand === "number" ? ~operand : null;
|
|
153
|
+
case ts.SyntaxKind.ExclamationToken:
|
|
154
|
+
return typeof operand === "boolean" ? !operand : null;
|
|
155
|
+
default:
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (ts.isBinaryExpression(node)) {
|
|
160
|
+
const left = evaluateConstantExpression(checker, node.left, seen);
|
|
161
|
+
if (left === null) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
const right = evaluateConstantExpression(checker, node.right, seen);
|
|
165
|
+
if (right === null) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
return foldBinaryExpression(node.operatorToken.kind, left, right);
|
|
169
|
+
}
|
|
170
|
+
if (ts.isTemplateExpression(node)) {
|
|
171
|
+
let result = node.head.text;
|
|
172
|
+
for (const span of node.templateSpans) {
|
|
173
|
+
const substitution = toFoldedString(evaluateConstantExpression(checker, span.expression, seen));
|
|
174
|
+
if (substitution === null) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
result += substitution + span.literal.text;
|
|
178
|
+
}
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression)) {
|
|
182
|
+
const namespaceConstant = constants_1.FOLDABLE_NAMESPACE_CONSTANTS[node.expression.text]?.[node.name.text];
|
|
183
|
+
if (namespaceConstant !== undefined) {
|
|
184
|
+
return namespaceConstant;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (ts.isIdentifier(node)) {
|
|
188
|
+
const symbol = checker.getSymbolAtLocation(node);
|
|
189
|
+
return symbol === undefined ? null : resolveConstantSymbol(checker, symbol, seen);
|
|
190
|
+
}
|
|
191
|
+
if (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)) {
|
|
192
|
+
const symbol = (0, ast_1.resolveMemberSymbol)(checker, node);
|
|
193
|
+
return symbol === null ? null : resolveConstantSymbol(checker, symbol, seen);
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Resolve referenced symbol to a compile-time constant value when the reference is safe to fold.
|
|
199
|
+
* Safe references are enum members, module-level const scalars and readonly `as const` object properties -
|
|
200
|
+
* values that cannot legally change at runtime. Tags are not required on referenced declarations.
|
|
201
|
+
*
|
|
202
|
+
* @param checker - Program type checker.
|
|
203
|
+
* @param symbol - Referenced symbol to resolve.
|
|
204
|
+
* @param seen - Set of declarations on current resolution path.
|
|
205
|
+
* @returns Folded constant value or null.
|
|
206
|
+
*/
|
|
207
|
+
function resolveConstantSymbol(checker, symbol, seen) {
|
|
208
|
+
const resolved = (symbol.flags & ts.SymbolFlags.Alias) !== 0 ? checker.getAliasedSymbol(symbol) : symbol;
|
|
209
|
+
const declaration = resolved.valueDeclaration;
|
|
210
|
+
if (declaration === undefined) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
if (ts.isEnumMember(declaration)) {
|
|
214
|
+
const value = checker.getConstantValue(declaration);
|
|
215
|
+
return typeof value === "string" || typeof value === "number" ? value : null;
|
|
216
|
+
}
|
|
217
|
+
if (ts.isVariableDeclaration(declaration)) {
|
|
218
|
+
const statement = (0, ast_1.getContainingVariableStatement)(declaration);
|
|
219
|
+
if (statement === null || !(0, ast_1.isModuleLevelConst)(statement)) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
return getComputedDeclarationValue(checker, resolved, seen);
|
|
223
|
+
}
|
|
224
|
+
if (ts.isPropertyAssignment(declaration) || ts.isShorthandPropertyAssignment(declaration)) {
|
|
225
|
+
return (0, ast_1.isReadonlyModuleConstProperty)(declaration) ? getComputedDeclarationValue(checker, resolved, seen) : null;
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get constant value of a declaration - declared unit type first, initializer folding as fallback.
|
|
231
|
+
* Safety and tagging requirements are checked by callers.
|
|
232
|
+
*
|
|
233
|
+
* @param checker - Program type checker.
|
|
234
|
+
* @param symbol - Symbol to get value for.
|
|
235
|
+
* @param seen - Set of declarations on current resolution path.
|
|
236
|
+
* @returns Constant value or null.
|
|
237
|
+
*/
|
|
238
|
+
function getComputedDeclarationValue(checker, symbol, seen) {
|
|
239
|
+
const declaration = symbol.valueDeclaration;
|
|
240
|
+
if (declaration === undefined) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
const declared = getDeclaredLiteralValue(checker, symbol);
|
|
244
|
+
if (declared !== null) {
|
|
245
|
+
return declared;
|
|
246
|
+
}
|
|
247
|
+
if (seen.has(declaration)) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
seen.add(declaration);
|
|
251
|
+
try {
|
|
252
|
+
if ((ts.isVariableDeclaration(declaration) || ts.isPropertyAssignment(declaration)) &&
|
|
253
|
+
declaration.initializer !== undefined) {
|
|
254
|
+
return evaluateConstantExpression(checker, declaration.initializer, seen);
|
|
255
|
+
}
|
|
256
|
+
if (ts.isShorthandPropertyAssignment(declaration)) {
|
|
257
|
+
const valueSymbol = checker.getShorthandAssignmentValueSymbol(declaration);
|
|
258
|
+
return valueSymbol === undefined ? null : resolveConstantSymbol(checker, valueSymbol, seen);
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
seen.delete(declaration);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Get inline-able literal value for a symbol resolved from an access expression or identifier.
|
|
268
|
+
* Only symbols declared inside `@inline` tagged enums / variable statements produce values.
|
|
269
|
+
*
|
|
270
|
+
* @param checker - Program type checker.
|
|
271
|
+
* @param symbol - Symbol to resolve inline value for.
|
|
272
|
+
* @returns Literal value or null.
|
|
273
|
+
*/
|
|
274
|
+
function tryGetInlineValue(checker, symbol) {
|
|
275
|
+
const declaration = symbol.valueDeclaration;
|
|
276
|
+
if (declaration === undefined) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
if (ts.isEnumMember(declaration)) {
|
|
280
|
+
if (!(0, ast_1.hasInlineTag)(declaration.parent)) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
const value = checker.getConstantValue(declaration);
|
|
284
|
+
return typeof value === "string" || typeof value === "number" ? value : null;
|
|
285
|
+
}
|
|
286
|
+
if (ts.isPropertyAssignment(declaration) ||
|
|
287
|
+
ts.isShorthandPropertyAssignment(declaration) ||
|
|
288
|
+
ts.isVariableDeclaration(declaration)) {
|
|
289
|
+
const statement = (0, ast_1.getContainingVariableStatement)(declaration);
|
|
290
|
+
if (statement === null || !(0, ast_1.hasInlineTag)(statement)) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
return getComputedDeclarationValue(checker, symbol, new Set());
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./ast"), exports);
|
|
18
|
+
__exportStar(require("./constants"), exports);
|
|
19
|
+
__exportStar(require("./errors"), exports);
|
|
20
|
+
__exportStar(require("./evaluation"), exports);
|
|
21
|
+
__exportStar(require("./transformers"), exports);
|
|
22
|
+
__exportStar(require("./validation"), exports);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import * as lua from "typescript-to-lua";
|
|
3
|
+
import { type TInlineValue } from "./constants";
|
|
4
|
+
/**
|
|
5
|
+
* Create lua literal expression for provided constant value.
|
|
6
|
+
*
|
|
7
|
+
* @param value - Constant value to create literal for.
|
|
8
|
+
* @param node - Original typescript node.
|
|
9
|
+
* @returns Lua literal expression.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createLiteralExpression(value: TInlineValue, node: ts.Node): lua.Expression;
|
|
12
|
+
/**
|
|
13
|
+
* Transform property/element access expressions, inline literal values of `@inline` tagged declarations.
|
|
14
|
+
*
|
|
15
|
+
* @param node - Access expression node to transform.
|
|
16
|
+
* @param context - Transformation context.
|
|
17
|
+
* @returns Lua literal when access resolves to a tagged constant, default transformation otherwise.
|
|
18
|
+
*/
|
|
19
|
+
export declare function transformAccessExpression(node: ts.PropertyAccessExpression | ts.ElementAccessExpression, context: lua.TransformationContext): lua.Expression;
|
|
20
|
+
/**
|
|
21
|
+
* Transform identifier expressions, inline literal values of `@inline` tagged scalar constants.
|
|
22
|
+
*
|
|
23
|
+
* @param node - Identifier node to transform.
|
|
24
|
+
* @param context - Transformation context.
|
|
25
|
+
* @returns Lua literal when identifier resolves to a tagged constant, default transformation otherwise.
|
|
26
|
+
*/
|
|
27
|
+
export declare function transformIdentifierExpression(node: ts.Identifier, context: lua.TransformationContext): lua.Expression;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLiteralExpression = createLiteralExpression;
|
|
4
|
+
exports.transformAccessExpression = transformAccessExpression;
|
|
5
|
+
exports.transformIdentifierExpression = transformIdentifierExpression;
|
|
6
|
+
const ts = require("typescript");
|
|
7
|
+
const lua = require("typescript-to-lua");
|
|
8
|
+
const ast_1 = require("./ast");
|
|
9
|
+
const evaluation_1 = require("./evaluation");
|
|
10
|
+
/**
|
|
11
|
+
* Create lua literal expression for provided constant value.
|
|
12
|
+
*
|
|
13
|
+
* @param value - Constant value to create literal for.
|
|
14
|
+
* @param node - Original typescript node.
|
|
15
|
+
* @returns Lua literal expression.
|
|
16
|
+
*/
|
|
17
|
+
function createLiteralExpression(value, node) {
|
|
18
|
+
if (typeof value === "string") {
|
|
19
|
+
return lua.createStringLiteral(value, node);
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === "boolean") {
|
|
22
|
+
return lua.createBooleanLiteral(value, node);
|
|
23
|
+
}
|
|
24
|
+
return value < 0
|
|
25
|
+
? lua.createUnaryExpression(lua.createNumericLiteral(Math.abs(value), node), lua.SyntaxKind.NegationOperator, node)
|
|
26
|
+
: lua.createNumericLiteral(value, node);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Transform property/element access expressions, inline literal values of `@inline` tagged declarations.
|
|
30
|
+
*
|
|
31
|
+
* @param node - Access expression node to transform.
|
|
32
|
+
* @param context - Transformation context.
|
|
33
|
+
* @returns Lua literal when access resolves to a tagged constant, default transformation otherwise.
|
|
34
|
+
*/
|
|
35
|
+
function transformAccessExpression(node, context) {
|
|
36
|
+
const symbol = (0, ast_1.resolveMemberSymbol)(context.checker, node);
|
|
37
|
+
const value = symbol === null ? null : (0, evaluation_1.tryGetInlineValue)(context.checker, symbol);
|
|
38
|
+
return value === null ? context.superTransformExpression(node) : createLiteralExpression(value, node);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Transform identifier expressions, inline literal values of `@inline` tagged scalar constants.
|
|
42
|
+
*
|
|
43
|
+
* @param node - Identifier node to transform.
|
|
44
|
+
* @param context - Transformation context.
|
|
45
|
+
* @returns Lua literal when identifier resolves to a tagged constant, default transformation otherwise.
|
|
46
|
+
*/
|
|
47
|
+
function transformIdentifierExpression(node, context) {
|
|
48
|
+
if ((0, ast_1.isValueUsagePosition)(node)) {
|
|
49
|
+
let symbol = context.checker.getSymbolAtLocation(node);
|
|
50
|
+
if (symbol !== undefined && (symbol.flags & ts.SymbolFlags.Alias) !== 0) {
|
|
51
|
+
symbol = context.checker.getAliasedSymbol(symbol);
|
|
52
|
+
}
|
|
53
|
+
if (symbol?.valueDeclaration !== undefined && ts.isVariableDeclaration(symbol.valueDeclaration)) {
|
|
54
|
+
const value = (0, evaluation_1.tryGetInlineValue)(context.checker, symbol);
|
|
55
|
+
if (value !== null) {
|
|
56
|
+
return createLiteralExpression(value, node);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return context.superTransformExpression(node);
|
|
61
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import type * as lua from "typescript-to-lua";
|
|
3
|
+
/**
|
|
4
|
+
* Validate `@inline` tagged variable statement and push diagnostics for not supported shapes.
|
|
5
|
+
* Tagged statements must be module-level `const` declarations with compile-time computable scalars
|
|
6
|
+
* or flat `as const` object literals.
|
|
7
|
+
*
|
|
8
|
+
* @param statement - Tagged variable statement to validate.
|
|
9
|
+
* @param context - Transformation context.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateVariableStatement(statement: ts.VariableStatement, context: lua.TransformationContext): void;
|
|
12
|
+
/**
|
|
13
|
+
* Validate `@inline` tagged enum declaration and push diagnostics for members without constant values.
|
|
14
|
+
*
|
|
15
|
+
* @param declaration - Tagged enum declaration to validate.
|
|
16
|
+
* @param context - Transformation context.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateEnumDeclaration(declaration: ts.EnumDeclaration, context: lua.TransformationContext): void;
|
|
19
|
+
/**
|
|
20
|
+
* Scan program source files for `@inline` tags on unsupported declaration kinds.
|
|
21
|
+
*
|
|
22
|
+
* @param program - Program to scan.
|
|
23
|
+
* @returns List of produced diagnostics.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateTopLevelTags(program: ts.Program): Array<ts.Diagnostic>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateVariableStatement = validateVariableStatement;
|
|
4
|
+
exports.validateEnumDeclaration = validateEnumDeclaration;
|
|
5
|
+
exports.validateTopLevelTags = validateTopLevelTags;
|
|
6
|
+
const ts = require("typescript");
|
|
7
|
+
const ast_1 = require("./ast");
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
const evaluation_1 = require("./evaluation");
|
|
10
|
+
/**
|
|
11
|
+
* Validate `@inline` tagged variable statement and push diagnostics for not supported shapes.
|
|
12
|
+
* Tagged statements must be module-level `const` declarations with compile-time computable scalars
|
|
13
|
+
* or flat `as const` object literals.
|
|
14
|
+
*
|
|
15
|
+
* @param statement - Tagged variable statement to validate.
|
|
16
|
+
* @param context - Transformation context.
|
|
17
|
+
*/
|
|
18
|
+
function validateVariableStatement(statement, context) {
|
|
19
|
+
if (!ts.isSourceFile(statement.parent)) {
|
|
20
|
+
context.diagnostics.push((0, errors_1.createNotModuleLevelError)(statement));
|
|
21
|
+
}
|
|
22
|
+
if ((statement.declarationList.flags & ts.NodeFlags.Const) === 0) {
|
|
23
|
+
context.diagnostics.push((0, errors_1.createNotConstError)(statement));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
27
|
+
const name = declaration.name.getText();
|
|
28
|
+
const initializer = (0, ast_1.unwrapInitializer)(declaration.initializer);
|
|
29
|
+
if (initializer !== null && ts.isObjectLiteralExpression(initializer)) {
|
|
30
|
+
if (!(0, ast_1.hasAsConstAssertion)(declaration.initializer)) {
|
|
31
|
+
context.diagnostics.push((0, errors_1.createNotAsConstObjectError)(declaration, name));
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const type = context.checker.getTypeAtLocation(declaration);
|
|
35
|
+
for (const property of type.getProperties()) {
|
|
36
|
+
const propertyDeclaration = property.valueDeclaration;
|
|
37
|
+
const sourceStatement = propertyDeclaration === undefined ? null : (0, ast_1.getContainingVariableStatement)(propertyDeclaration);
|
|
38
|
+
if (sourceStatement !== statement && (sourceStatement === null || !(0, ast_1.hasInlineTag)(sourceStatement))) {
|
|
39
|
+
context.diagnostics.push((0, errors_1.createForeignPropertyError)(propertyDeclaration ?? declaration, name, property.name));
|
|
40
|
+
}
|
|
41
|
+
else if ((0, evaluation_1.getComputedDeclarationValue)(context.checker, property, new Set()) === null) {
|
|
42
|
+
context.diagnostics.push((0, errors_1.createNotLiteralPropertyError)(propertyDeclaration ?? declaration, name, property.name));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const symbol = context.checker.getSymbolAtLocation(declaration.name);
|
|
48
|
+
if (symbol === undefined || (0, evaluation_1.getComputedDeclarationValue)(context.checker, symbol, new Set()) === null) {
|
|
49
|
+
context.diagnostics.push((0, errors_1.createNotLiteralConstantError)(declaration, name));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate `@inline` tagged enum declaration and push diagnostics for members without constant values.
|
|
56
|
+
*
|
|
57
|
+
* @param declaration - Tagged enum declaration to validate.
|
|
58
|
+
* @param context - Transformation context.
|
|
59
|
+
*/
|
|
60
|
+
function validateEnumDeclaration(declaration, context) {
|
|
61
|
+
for (const member of declaration.members) {
|
|
62
|
+
if (context.checker.getConstantValue(member) === undefined) {
|
|
63
|
+
context.diagnostics.push((0, errors_1.createNotConstantEnumMemberError)(member, member.name.getText()));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Scan program source files for `@inline` tags on unsupported declaration kinds.
|
|
69
|
+
*
|
|
70
|
+
* @param program - Program to scan.
|
|
71
|
+
* @returns List of produced diagnostics.
|
|
72
|
+
*/
|
|
73
|
+
function validateTopLevelTags(program) {
|
|
74
|
+
const diagnostics = [];
|
|
75
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
76
|
+
if (sourceFile.isDeclarationFile) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
for (const statement of sourceFile.statements) {
|
|
80
|
+
if ((0, ast_1.hasInlineTag)(statement) && !ts.isVariableStatement(statement) && !ts.isEnumDeclaration(statement)) {
|
|
81
|
+
diagnostics.push((0, errors_1.createUnsupportedDeclarationError)(statement));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return diagnostics;
|
|
86
|
+
}
|