@defold-typescript/transpiler 0.39.0 → 0.40.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/dist/array-index-option-lowering.d.ts +6 -0
- package/dist/index.js +264 -98
- package/package.json +2 -2
- package/src/array-index-option-lowering.ts +212 -0
- package/src/program-diagnostics.ts +2 -0
- package/src/session.ts +2 -0
- package/src/transpile.ts +2 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Plugin } from "typescript-to-lua";
|
|
2
|
+
export declare const ONE_BASED_INDEX_OPTION_APIS: ReadonlySet<string>;
|
|
3
|
+
export declare const INDEX_OPTION_VARIABLE_MESSAGE = "`options.index` is zero-based and is converted to Defold's 1-based index only in an object literal written at the call; pass the options inline.";
|
|
4
|
+
export declare const INDEX_OPTION_SPREAD_MESSAGE = "A spread carrying `index` cannot be converted to Defold's 1-based index; write `index` explicitly after the spread.";
|
|
5
|
+
export declare const INDEX_OPTION_UNDEFINED_MESSAGE = "`options.index` may be undefined here, so it cannot be converted to Defold's 1-based index; pass a number, or leave `index` out.";
|
|
6
|
+
export declare const arrayIndexOptionLoweringPlugin: Plugin;
|
package/dist/index.js
CHANGED
|
@@ -2058,16 +2058,179 @@ function computeOutputRel(rel, config, kind) {
|
|
|
2058
2058
|
return baseRel.replace(/\.ts$/, SCRIPT_SUFFIX_BY_KIND[kind]);
|
|
2059
2059
|
}
|
|
2060
2060
|
// src/program-diagnostics.ts
|
|
2061
|
-
import * as
|
|
2061
|
+
import * as ts9 from "typescript";
|
|
2062
2062
|
import * as tstl from "typescript-to-lua";
|
|
2063
2063
|
|
|
2064
|
-
// src/
|
|
2064
|
+
// src/array-index-option-lowering.ts
|
|
2065
2065
|
import * as ts5 from "typescript";
|
|
2066
2066
|
import {
|
|
2067
2067
|
createBinaryExpression as createBinaryExpression3,
|
|
2068
|
+
createNumericLiteral,
|
|
2069
|
+
getOriginalPos,
|
|
2070
|
+
isCallExpression as isCallExpression3,
|
|
2071
|
+
isNumericLiteral as isNumericLiteral3,
|
|
2072
|
+
isStringLiteral as isStringLiteral5,
|
|
2073
|
+
isTableExpression,
|
|
2074
|
+
SyntaxKind as SyntaxKind7,
|
|
2075
|
+
setNodePosition
|
|
2076
|
+
} from "typescript-to-lua";
|
|
2077
|
+
var ONE_BASED_INDEX_OPTION_APIS = new Set([
|
|
2078
|
+
"go.get",
|
|
2079
|
+
"go.set",
|
|
2080
|
+
"gui.get",
|
|
2081
|
+
"gui.set"
|
|
2082
|
+
]);
|
|
2083
|
+
var INDEX_OPTION_VARIABLE_MESSAGE = "`options.index` is zero-based and is converted to Defold's 1-based index only in an object literal written at the call; pass the options inline.";
|
|
2084
|
+
var INDEX_OPTION_SPREAD_MESSAGE = "A spread carrying `index` cannot be converted to Defold's 1-based index; write `index` explicitly after the spread.";
|
|
2085
|
+
var INDEX_OPTION_UNDEFINED_MESSAGE = "`options.index` may be undefined here, so it cannot be converted to Defold's 1-based index; pass a number, or leave `index` out.";
|
|
2086
|
+
var OPTIONS_PARAMETER = "options";
|
|
2087
|
+
var INDEX_KEY = "index";
|
|
2088
|
+
var loweredLiterals = new WeakSet;
|
|
2089
|
+
function oneBasedApi(declaration, checker) {
|
|
2090
|
+
let node = declaration;
|
|
2091
|
+
while (node !== undefined && !ts5.isFunctionDeclaration(node))
|
|
2092
|
+
node = node.parent;
|
|
2093
|
+
if (node?.name === undefined)
|
|
2094
|
+
return false;
|
|
2095
|
+
const symbol = checker.getSymbolAtLocation(node.name);
|
|
2096
|
+
if (symbol === undefined)
|
|
2097
|
+
return false;
|
|
2098
|
+
const fqn = checker.getFullyQualifiedName(symbol);
|
|
2099
|
+
return fqn.startsWith("global.") && ONE_BASED_INDEX_OPTION_APIS.has(fqn.slice("global.".length));
|
|
2100
|
+
}
|
|
2101
|
+
function optionsArgument(call, checker) {
|
|
2102
|
+
const declaration = checker.getResolvedSignature(call)?.declaration;
|
|
2103
|
+
if (declaration === undefined || !ts5.isFunctionLike(declaration))
|
|
2104
|
+
return;
|
|
2105
|
+
if (!oneBasedApi(declaration, checker))
|
|
2106
|
+
return;
|
|
2107
|
+
const index = declaration.parameters.findIndex((parameter) => parameter.dotDotDotToken === undefined && ts5.isIdentifier(parameter.name) && parameter.name.text === OPTIONS_PARAMETER);
|
|
2108
|
+
return index === -1 ? undefined : call.arguments[index];
|
|
2109
|
+
}
|
|
2110
|
+
function unwrap(expression) {
|
|
2111
|
+
let current = expression;
|
|
2112
|
+
while (ts5.isParenthesizedExpression(current) || ts5.isAsExpression(current) || ts5.isSatisfiesExpression(current) || ts5.isNonNullExpression(current) || ts5.isTypeAssertionExpression(current)) {
|
|
2113
|
+
current = current.expression;
|
|
2114
|
+
}
|
|
2115
|
+
return current;
|
|
2116
|
+
}
|
|
2117
|
+
function someConstituent(type, predicate) {
|
|
2118
|
+
return type.isUnion() ? type.types.some(predicate) : predicate(type);
|
|
2119
|
+
}
|
|
2120
|
+
function carriesIndex(type) {
|
|
2121
|
+
return someConstituent(type, (t) => t.getProperty(INDEX_KEY) !== undefined);
|
|
2122
|
+
}
|
|
2123
|
+
function admitsUndefined(type) {
|
|
2124
|
+
return someConstituent(type, (t) => (t.flags & (ts5.TypeFlags.Undefined | ts5.TypeFlags.Void)) !== 0);
|
|
2125
|
+
}
|
|
2126
|
+
function propertyName(property, checker) {
|
|
2127
|
+
const name = property.name;
|
|
2128
|
+
if (name === undefined)
|
|
2129
|
+
return;
|
|
2130
|
+
if (ts5.isIdentifier(name) || ts5.isStringLiteral(name) || ts5.isNumericLiteral(name)) {
|
|
2131
|
+
return name.text;
|
|
2132
|
+
}
|
|
2133
|
+
if (ts5.isComputedPropertyName(name)) {
|
|
2134
|
+
const type = checker.getTypeAtLocation(name.expression);
|
|
2135
|
+
if (type.isStringLiteral())
|
|
2136
|
+
return type.value;
|
|
2137
|
+
}
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
function indexValueType(property, checker) {
|
|
2141
|
+
if (ts5.isPropertyAssignment(property))
|
|
2142
|
+
return checker.getTypeAtLocation(property.initializer);
|
|
2143
|
+
if (ts5.isShorthandPropertyAssignment(property))
|
|
2144
|
+
return checker.getTypeAtLocation(property.name);
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
2147
|
+
function rejection(node, messageText) {
|
|
2148
|
+
const file = node.getSourceFile();
|
|
2149
|
+
const start = node.getStart(file);
|
|
2150
|
+
return {
|
|
2151
|
+
file,
|
|
2152
|
+
start,
|
|
2153
|
+
length: node.getEnd() - start,
|
|
2154
|
+
category: ts5.DiagnosticCategory.Error,
|
|
2155
|
+
code: 0,
|
|
2156
|
+
messageText
|
|
2157
|
+
};
|
|
2158
|
+
}
|
|
2159
|
+
function rejectOptions(options, checker) {
|
|
2160
|
+
const literal = unwrap(options);
|
|
2161
|
+
if (!ts5.isObjectLiteralExpression(literal)) {
|
|
2162
|
+
return carriesIndex(checker.getTypeAtLocation(literal)) ? rejection(options, INDEX_OPTION_VARIABLE_MESSAGE) : undefined;
|
|
2163
|
+
}
|
|
2164
|
+
let uncoveredSpread;
|
|
2165
|
+
let explicitIndex;
|
|
2166
|
+
for (const property of literal.properties) {
|
|
2167
|
+
if (ts5.isSpreadAssignment(property)) {
|
|
2168
|
+
if (carriesIndex(checker.getTypeAtLocation(property.expression)))
|
|
2169
|
+
uncoveredSpread = property;
|
|
2170
|
+
} else if (propertyName(property, checker) === INDEX_KEY) {
|
|
2171
|
+
uncoveredSpread = undefined;
|
|
2172
|
+
explicitIndex = property;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
if (uncoveredSpread !== undefined)
|
|
2176
|
+
return rejection(uncoveredSpread, INDEX_OPTION_SPREAD_MESSAGE);
|
|
2177
|
+
const valueType = explicitIndex && indexValueType(explicitIndex, checker);
|
|
2178
|
+
if (explicitIndex !== undefined && valueType !== undefined && admitsUndefined(valueType)) {
|
|
2179
|
+
return rejection(explicitIndex, INDEX_OPTION_UNDEFINED_MESSAGE);
|
|
2180
|
+
}
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
2183
|
+
function plusOne(value) {
|
|
2184
|
+
if (isNumericLiteral3(value)) {
|
|
2185
|
+
return setNodePosition(createNumericLiteral(value.value + 1), getOriginalPos(value));
|
|
2186
|
+
}
|
|
2187
|
+
return setNodePosition(createBinaryExpression3(value, createNumericLiteral(1), SyntaxKind7.AdditionOperator), getOriginalPos(value));
|
|
2188
|
+
}
|
|
2189
|
+
function offsetEffectiveIndexField(tables) {
|
|
2190
|
+
for (const field of tables.flatMap((table) => table.fields).reverse()) {
|
|
2191
|
+
if (field.key !== undefined && isStringLiteral5(field.key) && field.key.value === INDEX_KEY) {
|
|
2192
|
+
field.value = plusOne(field.value);
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
var arrayIndexOptionLoweringPlugin = {
|
|
2198
|
+
visitors: {
|
|
2199
|
+
[ts5.SyntaxKind.CallExpression]: (node, context) => {
|
|
2200
|
+
const options = optionsArgument(node, context.checker);
|
|
2201
|
+
if (options !== undefined) {
|
|
2202
|
+
const diagnostic = rejectOptions(options, context.checker);
|
|
2203
|
+
if (diagnostic !== undefined) {
|
|
2204
|
+
context.diagnostics.push(diagnostic);
|
|
2205
|
+
} else {
|
|
2206
|
+
const literal = unwrap(options);
|
|
2207
|
+
if (ts5.isObjectLiteralExpression(literal))
|
|
2208
|
+
loweredLiterals.add(literal);
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
return context.superTransformExpression(node);
|
|
2212
|
+
},
|
|
2213
|
+
[ts5.SyntaxKind.ObjectLiteralExpression]: (node, context) => {
|
|
2214
|
+
const result = context.superTransformExpression(node);
|
|
2215
|
+
if (!loweredLiterals.has(node))
|
|
2216
|
+
return result;
|
|
2217
|
+
if (isTableExpression(result)) {
|
|
2218
|
+
offsetEffectiveIndexField([result]);
|
|
2219
|
+
} else if (isCallExpression3(result)) {
|
|
2220
|
+
offsetEffectiveIndexField(result.params.filter(isTableExpression));
|
|
2221
|
+
}
|
|
2222
|
+
return result;
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
};
|
|
2226
|
+
|
|
2227
|
+
// src/message-guard-lowering.ts
|
|
2228
|
+
import * as ts6 from "typescript";
|
|
2229
|
+
import {
|
|
2230
|
+
createBinaryExpression as createBinaryExpression4,
|
|
2068
2231
|
createCallExpression as createCallExpression3,
|
|
2069
2232
|
createIdentifier as createIdentifier3,
|
|
2070
|
-
SyntaxKind as
|
|
2233
|
+
SyntaxKind as SyntaxKind9
|
|
2071
2234
|
} from "typescript-to-lua";
|
|
2072
2235
|
var GUARD_MODULE = "@defold-typescript/types";
|
|
2073
2236
|
var GUARD_NAME = "isMessage";
|
|
@@ -2076,7 +2239,7 @@ function resolvesToGuardExport(callee, checker) {
|
|
|
2076
2239
|
if (symbol === undefined) {
|
|
2077
2240
|
return false;
|
|
2078
2241
|
}
|
|
2079
|
-
if (symbol.flags &
|
|
2242
|
+
if (symbol.flags & ts6.SymbolFlags.Alias) {
|
|
2080
2243
|
symbol = checker.getAliasedSymbol(symbol);
|
|
2081
2244
|
}
|
|
2082
2245
|
if (symbol.getName() !== GUARD_NAME) {
|
|
@@ -2090,12 +2253,12 @@ function resolvesToGuardExport(callee, checker) {
|
|
|
2090
2253
|
}
|
|
2091
2254
|
var messageGuardLoweringPlugin = {
|
|
2092
2255
|
visitors: {
|
|
2093
|
-
[
|
|
2256
|
+
[ts6.SyntaxKind.CallExpression]: (node, context) => {
|
|
2094
2257
|
const [messageIdArg, , expectedArg] = node.arguments;
|
|
2095
2258
|
if (node.arguments.length === 3 && messageIdArg !== undefined && expectedArg !== undefined && resolvesToGuardExport(node.expression, context.checker)) {
|
|
2096
2259
|
const messageId = context.transformExpression(messageIdArg);
|
|
2097
2260
|
const expected = context.transformExpression(expectedArg);
|
|
2098
|
-
return
|
|
2261
|
+
return createBinaryExpression4(messageId, createCallExpression3(createIdentifier3("hash"), [expected]), SyntaxKind9.EqualityOperator, node);
|
|
2099
2262
|
}
|
|
2100
2263
|
return context.superTransformExpression(node);
|
|
2101
2264
|
}
|
|
@@ -2103,34 +2266,34 @@ var messageGuardLoweringPlugin = {
|
|
|
2103
2266
|
};
|
|
2104
2267
|
|
|
2105
2268
|
// src/timers-lowering.ts
|
|
2106
|
-
import * as
|
|
2269
|
+
import * as ts7 from "typescript";
|
|
2107
2270
|
import { LuaLibFeature } from "typescript-to-lua";
|
|
2108
2271
|
function isTimersSpecifier(node) {
|
|
2109
|
-
return
|
|
2272
|
+
return ts7.isStringLiteral(node) && node.text === TIMERS_MODULE_SPECIFIER;
|
|
2110
2273
|
}
|
|
2111
2274
|
function importsTimersModule(sourceFile) {
|
|
2112
|
-
return sourceFile.statements.some((statement) =>
|
|
2275
|
+
return sourceFile.statements.some((statement) => ts7.isImportDeclaration(statement) && isTimersSpecifier(statement.moduleSpecifier));
|
|
2113
2276
|
}
|
|
2114
2277
|
var TIMERS_REQUIRE_PATH = `@NoResolution:${TIMERS_REQUIRE_NAME}`;
|
|
2115
2278
|
var timersLoweringPlugin = {
|
|
2116
2279
|
visitors: {
|
|
2117
|
-
[
|
|
2280
|
+
[ts7.SyntaxKind.ImportDeclaration]: (node, context) => {
|
|
2118
2281
|
if (!isTimersSpecifier(node.moduleSpecifier)) {
|
|
2119
2282
|
return context.superTransformStatements(node);
|
|
2120
2283
|
}
|
|
2121
2284
|
context.usedLuaLibFeatures.add(LuaLibFeature.Promise);
|
|
2122
2285
|
context.usedLuaLibFeatures.add(LuaLibFeature.New);
|
|
2123
|
-
const rewritten =
|
|
2286
|
+
const rewritten = ts7.factory.updateImportDeclaration(node, node.modifiers, node.importClause, ts7.factory.createStringLiteral(TIMERS_REQUIRE_PATH), node.attributes);
|
|
2124
2287
|
return context.superTransformStatements(rewritten);
|
|
2125
2288
|
}
|
|
2126
2289
|
}
|
|
2127
2290
|
};
|
|
2128
2291
|
|
|
2129
2292
|
// src/window-event-guard-lowering.ts
|
|
2130
|
-
import * as
|
|
2293
|
+
import * as ts8 from "typescript";
|
|
2131
2294
|
import {
|
|
2132
|
-
createBinaryExpression as
|
|
2133
|
-
SyntaxKind as
|
|
2295
|
+
createBinaryExpression as createBinaryExpression5,
|
|
2296
|
+
SyntaxKind as SyntaxKind12
|
|
2134
2297
|
} from "typescript-to-lua";
|
|
2135
2298
|
var GUARD_MODULE2 = "@defold-typescript/types";
|
|
2136
2299
|
var GUARD_NAME2 = "isWindowEvent";
|
|
@@ -2139,7 +2302,7 @@ function resolvesToGuardExport2(callee, checker) {
|
|
|
2139
2302
|
if (symbol === undefined) {
|
|
2140
2303
|
return false;
|
|
2141
2304
|
}
|
|
2142
|
-
if (symbol.flags &
|
|
2305
|
+
if (symbol.flags & ts8.SymbolFlags.Alias) {
|
|
2143
2306
|
symbol = checker.getAliasedSymbol(symbol);
|
|
2144
2307
|
}
|
|
2145
2308
|
if (symbol.getName() !== GUARD_NAME2) {
|
|
@@ -2153,12 +2316,12 @@ function resolvesToGuardExport2(callee, checker) {
|
|
|
2153
2316
|
}
|
|
2154
2317
|
var windowEventGuardLoweringPlugin = {
|
|
2155
2318
|
visitors: {
|
|
2156
|
-
[
|
|
2319
|
+
[ts8.SyntaxKind.CallExpression]: (node, context) => {
|
|
2157
2320
|
const [eventArg, , expectedArg] = node.arguments;
|
|
2158
2321
|
if (node.arguments.length === 3 && eventArg !== undefined && expectedArg !== undefined && resolvesToGuardExport2(node.expression, context.checker)) {
|
|
2159
2322
|
const event = context.transformExpression(eventArg);
|
|
2160
2323
|
const expected = context.transformExpression(expectedArg);
|
|
2161
|
-
return
|
|
2324
|
+
return createBinaryExpression5(event, expected, SyntaxKind12.EqualityOperator, node);
|
|
2162
2325
|
}
|
|
2163
2326
|
return context.superTransformExpression(node);
|
|
2164
2327
|
}
|
|
@@ -2171,14 +2334,15 @@ var LUA_PLUGINS = [
|
|
|
2171
2334
|
messageGuardLoweringPlugin,
|
|
2172
2335
|
windowEventGuardLoweringPlugin,
|
|
2173
2336
|
messageDispatchLoweringPlugin,
|
|
2174
|
-
timersLoweringPlugin
|
|
2337
|
+
timersLoweringPlugin,
|
|
2338
|
+
arrayIndexOptionLoweringPlugin
|
|
2175
2339
|
];
|
|
2176
2340
|
var noopWriteFile = () => {};
|
|
2177
2341
|
function isAmbient(fileName) {
|
|
2178
2342
|
return /[\\/]node_modules[\\/]/.test(fileName) || /(^|[\\/])lib\.[^\\/]*\.d\.ts$/.test(fileName);
|
|
2179
2343
|
}
|
|
2180
2344
|
function getProgramDiagnostics(program, sourceFile) {
|
|
2181
|
-
const { diagnostics } = tstl.getProgramTranspileResult(
|
|
2345
|
+
const { diagnostics } = tstl.getProgramTranspileResult(ts9.sys, noopWriteFile, {
|
|
2182
2346
|
program,
|
|
2183
2347
|
plugins: LUA_PLUGINS,
|
|
2184
2348
|
...sourceFile ? { sourceFiles: [sourceFile] } : {}
|
|
@@ -2749,11 +2913,11 @@ function relativeAddressesFrom(index, context) {
|
|
|
2749
2913
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
2750
2914
|
import { createRequire as createRequire2 } from "node:module";
|
|
2751
2915
|
import * as path3 from "node:path";
|
|
2752
|
-
import * as
|
|
2916
|
+
import * as ts13 from "typescript";
|
|
2753
2917
|
import * as tstl3 from "typescript-to-lua";
|
|
2754
2918
|
|
|
2755
2919
|
// src/companion-emit.ts
|
|
2756
|
-
import * as
|
|
2920
|
+
import * as ts10 from "typescript";
|
|
2757
2921
|
import {
|
|
2758
2922
|
createAssignmentStatement as createAssignmentStatement2,
|
|
2759
2923
|
createCallExpression as createCallExpression4,
|
|
@@ -2766,18 +2930,18 @@ import {
|
|
|
2766
2930
|
createTableIndexExpression as createTableIndexExpression2,
|
|
2767
2931
|
createVariableDeclarationStatement as createVariableDeclarationStatement3,
|
|
2768
2932
|
isBlock as isBlock3,
|
|
2769
|
-
isCallExpression as
|
|
2933
|
+
isCallExpression as isCallExpression5,
|
|
2770
2934
|
isDoStatement,
|
|
2771
2935
|
isForInStatement as isForInStatement2,
|
|
2772
2936
|
isForStatement,
|
|
2773
2937
|
isFunctionDefinition,
|
|
2774
2938
|
isFunctionExpression as isFunctionExpression4,
|
|
2775
|
-
isIdentifier as
|
|
2939
|
+
isIdentifier as isIdentifier5,
|
|
2776
2940
|
isMethodCallExpression,
|
|
2777
2941
|
isRepeatStatement,
|
|
2778
2942
|
isReturnStatement as isReturnStatement2,
|
|
2779
|
-
isStringLiteral as
|
|
2780
|
-
isTableExpression,
|
|
2943
|
+
isStringLiteral as isStringLiteral8,
|
|
2944
|
+
isTableExpression as isTableExpression2,
|
|
2781
2945
|
isVariableDeclarationStatement,
|
|
2782
2946
|
LuaPrinter
|
|
2783
2947
|
} from "typescript-to-lua";
|
|
@@ -2787,8 +2951,8 @@ function exportsIdentifier() {
|
|
|
2787
2951
|
}
|
|
2788
2952
|
function isRuntimeScriptSource(sourceFile, checker) {
|
|
2789
2953
|
return sourceFile.statements.some((statement) => {
|
|
2790
|
-
const expression =
|
|
2791
|
-
return expression !== undefined &&
|
|
2954
|
+
const expression = ts10.isExpressionStatement(statement) ? statement.expression : ts10.isExportAssignment(statement) && !statement.isExportEquals ? statement.expression : undefined;
|
|
2955
|
+
return expression !== undefined && ts10.isCallExpression(expression) && resolvesToFactoryExport(expression.expression, checker);
|
|
2792
2956
|
});
|
|
2793
2957
|
}
|
|
2794
2958
|
function planFor(sourceFile, checker) {
|
|
@@ -2849,7 +3013,7 @@ function freeNames(statements) {
|
|
|
2849
3013
|
if (node === undefined) {
|
|
2850
3014
|
return;
|
|
2851
3015
|
}
|
|
2852
|
-
if (
|
|
3016
|
+
if (isIdentifier5(node)) {
|
|
2853
3017
|
if (!isBound(node.text)) {
|
|
2854
3018
|
free.add(node.text);
|
|
2855
3019
|
}
|
|
@@ -2940,11 +3104,11 @@ function freeNames(statements) {
|
|
|
2940
3104
|
function resolvedRequirePaths(sourceFile, checker) {
|
|
2941
3105
|
const paths = new Map;
|
|
2942
3106
|
for (const statement of sourceFile.statements) {
|
|
2943
|
-
const specifier =
|
|
2944
|
-
if (specifier === undefined || !
|
|
3107
|
+
const specifier = ts10.isImportDeclaration(statement) || ts10.isExportDeclaration(statement) ? statement.moduleSpecifier : undefined;
|
|
3108
|
+
if (specifier === undefined || !ts10.isStringLiteral(specifier)) {
|
|
2945
3109
|
continue;
|
|
2946
3110
|
}
|
|
2947
|
-
const target = checker.getSymbolAtLocation(specifier)?.declarations?.find((declaration) =>
|
|
3111
|
+
const target = checker.getSymbolAtLocation(specifier)?.declarations?.find((declaration) => ts10.isSourceFile(declaration));
|
|
2948
3112
|
if (target === undefined || target.isDeclarationFile) {
|
|
2949
3113
|
continue;
|
|
2950
3114
|
}
|
|
@@ -2955,14 +3119,14 @@ function resolvedRequirePaths(sourceFile, checker) {
|
|
|
2955
3119
|
function resolveMovedRequires(statements, paths) {
|
|
2956
3120
|
for (const statement of statements) {
|
|
2957
3121
|
forEachLuaNode(statement, (node) => {
|
|
2958
|
-
if (!
|
|
3122
|
+
if (!isCallExpression5(node) || !isIdentifier5(node.expression)) {
|
|
2959
3123
|
return;
|
|
2960
3124
|
}
|
|
2961
3125
|
if (node.expression.text !== "require" || node.params.length !== 1) {
|
|
2962
3126
|
return;
|
|
2963
3127
|
}
|
|
2964
3128
|
const [argument] = node.params;
|
|
2965
|
-
if (argument === undefined || !
|
|
3129
|
+
if (argument === undefined || !isStringLiteral8(argument)) {
|
|
2966
3130
|
return;
|
|
2967
3131
|
}
|
|
2968
3132
|
const resolved = paths.get(argument.value);
|
|
@@ -2978,14 +3142,14 @@ function withResolvedRequire(statement, paths) {
|
|
|
2978
3142
|
}
|
|
2979
3143
|
const name = statement.left[0];
|
|
2980
3144
|
const [value] = statement.right ?? [];
|
|
2981
|
-
if (name === undefined || value === undefined || !
|
|
3145
|
+
if (name === undefined || value === undefined || !isCallExpression5(value)) {
|
|
2982
3146
|
return statement;
|
|
2983
3147
|
}
|
|
2984
|
-
if (!
|
|
3148
|
+
if (!isIdentifier5(value.expression) || value.expression.text !== "require") {
|
|
2985
3149
|
return statement;
|
|
2986
3150
|
}
|
|
2987
3151
|
const [argument] = value.params;
|
|
2988
|
-
if (argument === undefined || !
|
|
3152
|
+
if (argument === undefined || !isStringLiteral8(argument)) {
|
|
2989
3153
|
return statement;
|
|
2990
3154
|
}
|
|
2991
3155
|
const resolved = paths.get(argument.value);
|
|
@@ -3021,7 +3185,7 @@ function copiedPrelude(halves, importBindings) {
|
|
|
3021
3185
|
function namesIdentifier(statement, names) {
|
|
3022
3186
|
let found = false;
|
|
3023
3187
|
forEachLuaNode(statement, (node) => {
|
|
3024
|
-
if (
|
|
3188
|
+
if (isIdentifier5(node) && names.has(node.text)) {
|
|
3025
3189
|
found = true;
|
|
3026
3190
|
}
|
|
3027
3191
|
});
|
|
@@ -3032,14 +3196,14 @@ function isExportsTableInit(statement) {
|
|
|
3032
3196
|
return false;
|
|
3033
3197
|
}
|
|
3034
3198
|
const [single] = statement.right ?? [];
|
|
3035
|
-
return statement.left[0]?.text === EXPORTS_NAME && single !== undefined &&
|
|
3199
|
+
return statement.left[0]?.text === EXPORTS_NAME && single !== undefined && isTableExpression2(single);
|
|
3036
3200
|
}
|
|
3037
3201
|
function isExportsReturn(statement) {
|
|
3038
3202
|
if (!isReturnStatement2(statement) || statement.expressions.length !== 1) {
|
|
3039
3203
|
return false;
|
|
3040
3204
|
}
|
|
3041
3205
|
const [single] = statement.expressions;
|
|
3042
|
-
return single !== undefined &&
|
|
3206
|
+
return single !== undefined && isIdentifier5(single) && single.text === EXPORTS_NAME;
|
|
3043
3207
|
}
|
|
3044
3208
|
function isHoistedNameDeclaration(statement) {
|
|
3045
3209
|
return isVariableDeclarationStatement(statement) && statement.right === undefined;
|
|
@@ -3108,11 +3272,11 @@ function createCompanionEmitPlugin() {
|
|
|
3108
3272
|
return statements;
|
|
3109
3273
|
}
|
|
3110
3274
|
const visitors = {
|
|
3111
|
-
[
|
|
3112
|
-
[
|
|
3113
|
-
[
|
|
3114
|
-
[
|
|
3115
|
-
[
|
|
3275
|
+
[ts10.SyntaxKind.VariableStatement]: capture,
|
|
3276
|
+
[ts10.SyntaxKind.FunctionDeclaration]: capture,
|
|
3277
|
+
[ts10.SyntaxKind.ClassDeclaration]: capture,
|
|
3278
|
+
[ts10.SyntaxKind.EnumDeclaration]: capture,
|
|
3279
|
+
[ts10.SyntaxKind.ExportDeclaration]: capture
|
|
3116
3280
|
};
|
|
3117
3281
|
let configuredNoResolvePaths = [];
|
|
3118
3282
|
const plugin = {
|
|
@@ -3181,7 +3345,7 @@ function emitCompanion(program, emitHost, file, sourceFile, plan, owned, compani
|
|
|
3181
3345
|
}
|
|
3182
3346
|
|
|
3183
3347
|
// src/editor-script-erasure.ts
|
|
3184
|
-
import * as
|
|
3348
|
+
import * as ts11 from "typescript";
|
|
3185
3349
|
import {
|
|
3186
3350
|
createReturnStatement as createReturnStatement3
|
|
3187
3351
|
} from "typescript-to-lua";
|
|
@@ -3201,7 +3365,7 @@ function resolveEditorFactoryExport(callee, checker) {
|
|
|
3201
3365
|
if (symbol === undefined) {
|
|
3202
3366
|
return;
|
|
3203
3367
|
}
|
|
3204
|
-
if (symbol.flags &
|
|
3368
|
+
if (symbol.flags & ts11.SymbolFlags.Alias) {
|
|
3205
3369
|
symbol = checker.getAliasedSymbol(symbol);
|
|
3206
3370
|
}
|
|
3207
3371
|
const name = symbol.getName();
|
|
@@ -3215,7 +3379,7 @@ function resolveEditorFactoryExport(callee, checker) {
|
|
|
3215
3379
|
return declaration.getSourceFile().fileName.includes(EDITOR_FACTORY_MODULE) ? name : undefined;
|
|
3216
3380
|
}
|
|
3217
3381
|
function lowerEditorFactoryCall(expression, context) {
|
|
3218
|
-
if (!
|
|
3382
|
+
if (!ts11.isCallExpression(expression)) {
|
|
3219
3383
|
return;
|
|
3220
3384
|
}
|
|
3221
3385
|
if (resolveEditorFactoryExport(expression.expression, context.checker) !== EDITOR_SCRIPT_FACTORY_NAME) {
|
|
@@ -3228,7 +3392,7 @@ function lowerEditorFactoryCall(expression, context) {
|
|
|
3228
3392
|
return [createReturnStatement3([context.transformExpression(module)])];
|
|
3229
3393
|
}
|
|
3230
3394
|
function isEditorFactoryOnlyImport(node) {
|
|
3231
|
-
if (!
|
|
3395
|
+
if (!ts11.isStringLiteral(node.moduleSpecifier) || !EDITOR_FACTORY_SPECIFIERS.has(node.moduleSpecifier.text)) {
|
|
3232
3396
|
return false;
|
|
3233
3397
|
}
|
|
3234
3398
|
const clause = node.importClause;
|
|
@@ -3239,7 +3403,7 @@ function isEditorFactoryOnlyImport(node) {
|
|
|
3239
3403
|
return false;
|
|
3240
3404
|
}
|
|
3241
3405
|
const bindings = clause.namedBindings;
|
|
3242
|
-
if (bindings === undefined || !
|
|
3406
|
+
if (bindings === undefined || !ts11.isNamedImports(bindings)) {
|
|
3243
3407
|
return false;
|
|
3244
3408
|
}
|
|
3245
3409
|
const runtime = bindings.elements.filter((element) => !element.isTypeOnly);
|
|
@@ -3250,7 +3414,7 @@ function isEditorFactoryOnlyImport(node) {
|
|
|
3250
3414
|
}
|
|
3251
3415
|
var editorScriptErasurePlugin = {
|
|
3252
3416
|
visitors: {
|
|
3253
|
-
[
|
|
3417
|
+
[ts11.SyntaxKind.ExportAssignment]: (node, context) => {
|
|
3254
3418
|
if (!node.isExportEquals) {
|
|
3255
3419
|
const lowered = lowerEditorFactoryCall(node.expression, context);
|
|
3256
3420
|
if (lowered !== undefined) {
|
|
@@ -3259,14 +3423,14 @@ var editorScriptErasurePlugin = {
|
|
|
3259
3423
|
}
|
|
3260
3424
|
return context.superTransformStatements(node);
|
|
3261
3425
|
},
|
|
3262
|
-
[
|
|
3426
|
+
[ts11.SyntaxKind.CallExpression]: (node, context) => {
|
|
3263
3427
|
const command = node.arguments[0];
|
|
3264
3428
|
if (command !== undefined && resolveEditorFactoryExport(node.expression, context.checker) === EDITOR_COMMAND_FACTORY_NAME) {
|
|
3265
3429
|
return context.transformExpression(command);
|
|
3266
3430
|
}
|
|
3267
3431
|
return context.superTransformExpression(node);
|
|
3268
3432
|
},
|
|
3269
|
-
[
|
|
3433
|
+
[ts11.SyntaxKind.ImportDeclaration]: (node, context) => isEditorFactoryOnlyImport(node) ? [] : context.superTransformStatements(node)
|
|
3270
3434
|
}
|
|
3271
3435
|
};
|
|
3272
3436
|
|
|
@@ -3277,17 +3441,17 @@ import * as path2 from "node:path";
|
|
|
3277
3441
|
import * as tstl2 from "typescript-to-lua";
|
|
3278
3442
|
|
|
3279
3443
|
// src/go-property-direct-call.ts
|
|
3280
|
-
import * as
|
|
3444
|
+
import * as ts12 from "typescript";
|
|
3281
3445
|
var GO_PROPERTY_DIRECT_CALL_MESSAGE = "`go.property` called directly is deprecated; declare the property in `defineScript({ properties })` so it types onto `self` (the transpiler emits the registration for you).";
|
|
3282
3446
|
function findDirectGoPropertyCalls(sourceFile) {
|
|
3283
3447
|
const calls = [];
|
|
3284
3448
|
const visit = (node) => {
|
|
3285
|
-
if (
|
|
3449
|
+
if (ts12.isCallExpression(node) && ts12.isPropertyAccessExpression(node.expression) && node.expression.name.text === "property" && ts12.isIdentifier(node.expression.expression) && node.expression.expression.text === "go") {
|
|
3286
3450
|
calls.push(node);
|
|
3287
3451
|
}
|
|
3288
|
-
|
|
3452
|
+
ts12.forEachChild(node, visit);
|
|
3289
3453
|
};
|
|
3290
|
-
|
|
3454
|
+
ts12.forEachChild(sourceFile, visit);
|
|
3291
3455
|
return calls;
|
|
3292
3456
|
}
|
|
3293
3457
|
|
|
@@ -3467,6 +3631,7 @@ function transpileProject(input) {
|
|
|
3467
3631
|
{ plugin: windowEventGuardLoweringPlugin },
|
|
3468
3632
|
{ plugin: messageDispatchLoweringPlugin },
|
|
3469
3633
|
{ plugin: timersLoweringPlugin },
|
|
3634
|
+
{ plugin: arrayIndexOptionLoweringPlugin },
|
|
3470
3635
|
{ plugin: companionEmit.plugin }
|
|
3471
3636
|
]
|
|
3472
3637
|
});
|
|
@@ -3497,6 +3662,7 @@ function compilerOptions(companionEmit) {
|
|
|
3497
3662
|
{ plugin: windowEventGuardLoweringPlugin },
|
|
3498
3663
|
{ plugin: messageDispatchLoweringPlugin },
|
|
3499
3664
|
{ plugin: timersLoweringPlugin },
|
|
3665
|
+
{ plugin: arrayIndexOptionLoweringPlugin },
|
|
3500
3666
|
{ plugin: companionEmit.plugin }
|
|
3501
3667
|
]
|
|
3502
3668
|
};
|
|
@@ -3539,7 +3705,7 @@ function createTranspileSession() {
|
|
|
3539
3705
|
if (cached) {
|
|
3540
3706
|
return cached;
|
|
3541
3707
|
}
|
|
3542
|
-
const created =
|
|
3708
|
+
const created = ts13.createSourceFile(fileName, content, ts13.ScriptTarget.Latest, false);
|
|
3543
3709
|
sourceFileCache.set(normalized, created);
|
|
3544
3710
|
return created;
|
|
3545
3711
|
}
|
|
@@ -3558,7 +3724,7 @@ function createTranspileSession() {
|
|
|
3558
3724
|
return cached;
|
|
3559
3725
|
}
|
|
3560
3726
|
const fileContent = readFileSync2(filePath, "utf8");
|
|
3561
|
-
const created =
|
|
3727
|
+
const created = ts13.createSourceFile(filePath, fileContent, ts13.ScriptTarget.Latest, false);
|
|
3562
3728
|
libCache.set(fileName, created);
|
|
3563
3729
|
return created;
|
|
3564
3730
|
}
|
|
@@ -3571,10 +3737,10 @@ function createTranspileSession() {
|
|
|
3571
3737
|
return AMBIENT_FILES[normalized];
|
|
3572
3738
|
}
|
|
3573
3739
|
const host = {
|
|
3574
|
-
fileExists: (fileName) => mergedContent(normalizeSlashes(fileName)) !== undefined ||
|
|
3740
|
+
fileExists: (fileName) => mergedContent(normalizeSlashes(fileName)) !== undefined || ts13.sys.fileExists(fileName),
|
|
3575
3741
|
getCanonicalFileName: (fileName) => fileName,
|
|
3576
3742
|
getCurrentDirectory: () => "",
|
|
3577
|
-
getDefaultLibFileName:
|
|
3743
|
+
getDefaultLibFileName: ts13.getDefaultLibFileName,
|
|
3578
3744
|
readFile: () => "",
|
|
3579
3745
|
getNewLine: () => `
|
|
3580
3746
|
`,
|
|
@@ -3596,15 +3762,15 @@ function createTranspileSession() {
|
|
|
3596
3762
|
const userKeys = new Set(userFiles.keys());
|
|
3597
3763
|
const rootNames = [...Object.keys(AMBIENT_FILES).map(normalizeSlashes), ...userKeys];
|
|
3598
3764
|
const companionEmit = createCompanionEmitPlugin();
|
|
3599
|
-
program =
|
|
3600
|
-
const preEmitDiagnostics =
|
|
3765
|
+
program = ts13.createProgram(rootNames, compilerOptions(companionEmit), host, program);
|
|
3766
|
+
const preEmitDiagnostics = ts13.getPreEmitDiagnostics(program);
|
|
3601
3767
|
const collector = createOutputCollector();
|
|
3602
3768
|
const { diagnostics: transpileDiagnostics } = new tstl3.Transpiler().emit({
|
|
3603
3769
|
program,
|
|
3604
3770
|
writeFile: collector.writeFile
|
|
3605
3771
|
});
|
|
3606
3772
|
const diagnostics = [
|
|
3607
|
-
...
|
|
3773
|
+
...ts13.sortAndDeduplicateDiagnostics([...preEmitDiagnostics, ...transpileDiagnostics])
|
|
3608
3774
|
];
|
|
3609
3775
|
return collectOutputs(collector.files, diagnostics, userKeys, companionEmit.companions);
|
|
3610
3776
|
}
|
|
@@ -3614,7 +3780,7 @@ function createTranspileSession() {
|
|
|
3614
3780
|
};
|
|
3615
3781
|
}
|
|
3616
3782
|
// src/url-address-slots.ts
|
|
3617
|
-
import * as
|
|
3783
|
+
import * as ts14 from "typescript";
|
|
3618
3784
|
var ADDRESS_CLASSES = {
|
|
3619
3785
|
"game-object": true,
|
|
3620
3786
|
component: true,
|
|
@@ -3633,7 +3799,7 @@ function isFragmentCaret(slot, position) {
|
|
|
3633
3799
|
return slot.fragmentStart !== -1 && position >= slot.fragmentStart;
|
|
3634
3800
|
}
|
|
3635
3801
|
function tableKey(checker, callee) {
|
|
3636
|
-
const symbol = checker.getSymbolAtLocation(
|
|
3802
|
+
const symbol = checker.getSymbolAtLocation(ts14.isIdentifier(callee) ? callee : callee.name);
|
|
3637
3803
|
if (!symbol)
|
|
3638
3804
|
return;
|
|
3639
3805
|
const fqn = checker.getFullyQualifiedName(symbol);
|
|
@@ -3641,43 +3807,43 @@ function tableKey(checker, callee) {
|
|
|
3641
3807
|
}
|
|
3642
3808
|
function parameterName(checker, call, index) {
|
|
3643
3809
|
const declaration = checker.getResolvedSignature(call)?.declaration;
|
|
3644
|
-
if (!declaration || !
|
|
3810
|
+
if (!declaration || !ts14.isFunctionLike(declaration))
|
|
3645
3811
|
return;
|
|
3646
3812
|
const parameter = declaration.parameters[index];
|
|
3647
|
-
if (!parameter || parameter.dotDotDotToken || !
|
|
3813
|
+
if (!parameter || parameter.dotDotDotToken || !ts14.isIdentifier(parameter.name))
|
|
3648
3814
|
return;
|
|
3649
3815
|
return parameter.name.text;
|
|
3650
3816
|
}
|
|
3651
3817
|
var EQUALITY_OPERATORS = new Set([
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3818
|
+
ts14.SyntaxKind.EqualsEqualsEqualsToken,
|
|
3819
|
+
ts14.SyntaxKind.ExclamationEqualsEqualsToken,
|
|
3820
|
+
ts14.SyntaxKind.EqualsEqualsToken,
|
|
3821
|
+
ts14.SyntaxKind.ExclamationEqualsToken
|
|
3656
3822
|
]);
|
|
3657
3823
|
function comparisonSatisfied(checker, entry, call) {
|
|
3658
3824
|
if (entry.comparedParameter === undefined)
|
|
3659
3825
|
return true;
|
|
3660
3826
|
let node = call;
|
|
3661
|
-
while (
|
|
3827
|
+
while (ts14.isParenthesizedExpression(node.parent))
|
|
3662
3828
|
node = node.parent;
|
|
3663
3829
|
const comparison = node.parent;
|
|
3664
|
-
if (!
|
|
3830
|
+
if (!ts14.isBinaryExpression(comparison))
|
|
3665
3831
|
return false;
|
|
3666
3832
|
if (!EQUALITY_OPERATORS.has(comparison.operatorToken.kind))
|
|
3667
3833
|
return false;
|
|
3668
3834
|
const other = comparison.left === node ? comparison.right : comparison.left;
|
|
3669
3835
|
const symbol = checker.getSymbolAtLocation(other);
|
|
3670
|
-
return (symbol?.declarations ?? []).some((declaration) =>
|
|
3836
|
+
return (symbol?.declarations ?? []).some((declaration) => ts14.isParameter(declaration) && ts14.isIdentifier(declaration.name) && declaration.name.text === entry.comparedParameter);
|
|
3671
3837
|
}
|
|
3672
3838
|
function classifiedEntryOfArgument(checker, table, argument) {
|
|
3673
3839
|
const call = argument.parent;
|
|
3674
|
-
if (!
|
|
3840
|
+
if (!ts14.isCallExpression(call))
|
|
3675
3841
|
return;
|
|
3676
3842
|
const argumentIndex = call.arguments.indexOf(argument);
|
|
3677
3843
|
if (argumentIndex === -1)
|
|
3678
3844
|
return;
|
|
3679
3845
|
const callee = call.expression;
|
|
3680
|
-
if (!
|
|
3846
|
+
if (!ts14.isPropertyAccessExpression(callee) && !ts14.isIdentifier(callee))
|
|
3681
3847
|
return;
|
|
3682
3848
|
const fqn = tableKey(checker, callee);
|
|
3683
3849
|
if (fqn === undefined)
|
|
@@ -3694,10 +3860,10 @@ function classifiedEntryOfArgument(checker, table, argument) {
|
|
|
3694
3860
|
}
|
|
3695
3861
|
function slotThroughHashedCall(checker, table, literal) {
|
|
3696
3862
|
const call = literal.parent;
|
|
3697
|
-
if (!
|
|
3863
|
+
if (!ts14.isCallExpression(call))
|
|
3698
3864
|
return;
|
|
3699
3865
|
const callee = call.expression;
|
|
3700
|
-
if (!
|
|
3866
|
+
if (!ts14.isPropertyAccessExpression(callee) && !ts14.isIdentifier(callee))
|
|
3701
3867
|
return;
|
|
3702
3868
|
if (tableKey(checker, callee) !== "hash")
|
|
3703
3869
|
return;
|
|
@@ -3715,24 +3881,24 @@ function addressTextOf(checker, entry, call) {
|
|
|
3715
3881
|
if (entry.addressParameter === undefined)
|
|
3716
3882
|
return;
|
|
3717
3883
|
const declaration = checker.getResolvedSignature(call)?.declaration;
|
|
3718
|
-
if (!declaration || !
|
|
3884
|
+
if (!declaration || !ts14.isFunctionLike(declaration))
|
|
3719
3885
|
return;
|
|
3720
|
-
const index = declaration.parameters.findIndex((parameter) => !parameter.dotDotDotToken &&
|
|
3886
|
+
const index = declaration.parameters.findIndex((parameter) => !parameter.dotDotDotToken && ts14.isIdentifier(parameter.name) && parameter.name.text === entry.addressParameter);
|
|
3721
3887
|
if (index === -1)
|
|
3722
3888
|
return;
|
|
3723
3889
|
const argument = call.arguments[index];
|
|
3724
|
-
return argument &&
|
|
3890
|
+
return argument && ts14.isStringLiteralLike(argument) ? argument.text : undefined;
|
|
3725
3891
|
}
|
|
3726
3892
|
function innermostLiteralAt(sourceFile, position) {
|
|
3727
3893
|
let found;
|
|
3728
3894
|
const visit = (node) => {
|
|
3729
3895
|
if (position < node.getStart(sourceFile) || position > node.getEnd())
|
|
3730
3896
|
return;
|
|
3731
|
-
if (
|
|
3897
|
+
if (ts14.isStringLiteralLike(node))
|
|
3732
3898
|
found = node;
|
|
3733
|
-
|
|
3899
|
+
ts14.forEachChild(node, visit);
|
|
3734
3900
|
};
|
|
3735
|
-
|
|
3901
|
+
ts14.forEachChild(sourceFile, visit);
|
|
3736
3902
|
return found;
|
|
3737
3903
|
}
|
|
3738
3904
|
function resolveClassifiedSlotAtPosition(input) {
|
|
@@ -3766,23 +3932,23 @@ function resolveClassifiedSlotAtPosition(input) {
|
|
|
3766
3932
|
};
|
|
3767
3933
|
}
|
|
3768
3934
|
// src/url-cross-world-address.ts
|
|
3769
|
-
import * as
|
|
3935
|
+
import * as ts16 from "typescript";
|
|
3770
3936
|
|
|
3771
3937
|
// src/url-address-literals.ts
|
|
3772
|
-
import * as
|
|
3938
|
+
import * as ts15 from "typescript";
|
|
3773
3939
|
function isAmbient2(fileName) {
|
|
3774
3940
|
return /[\\/]node_modules[\\/]/.test(fileName) || /(^|[\\/])lib\.[^\\/]*\.d\.ts$/.test(fileName);
|
|
3775
3941
|
}
|
|
3776
3942
|
function referenceTargetSymbol(type) {
|
|
3777
|
-
if ((type.flags &
|
|
3943
|
+
if ((type.flags & ts15.TypeFlags.Object) === 0)
|
|
3778
3944
|
return;
|
|
3779
|
-
if ((type.objectFlags &
|
|
3945
|
+
if ((type.objectFlags & ts15.ObjectFlags.Reference) === 0)
|
|
3780
3946
|
return;
|
|
3781
3947
|
return type.target.symbol;
|
|
3782
3948
|
}
|
|
3783
3949
|
function ambientHashAliasTargets(checker, anchor) {
|
|
3784
3950
|
const targets = new Set;
|
|
3785
|
-
for (const symbol of checker.getSymbolsInScope(anchor,
|
|
3951
|
+
for (const symbol of checker.getSymbolsInScope(anchor, ts15.SymbolFlags.Type)) {
|
|
3786
3952
|
if (symbol.name !== "Hash")
|
|
3787
3953
|
continue;
|
|
3788
3954
|
const target = referenceTargetSymbol(checker.getDeclaredTypeOfSymbol(symbol));
|
|
@@ -3798,7 +3964,7 @@ function canonicalHashSymbols(checker, program) {
|
|
|
3798
3964
|
if (!anchor)
|
|
3799
3965
|
return canonical;
|
|
3800
3966
|
const aliased = ambientHashAliasTargets(checker, anchor);
|
|
3801
|
-
for (const symbol of checker.getSymbolsInScope(anchor,
|
|
3967
|
+
for (const symbol of checker.getSymbolsInScope(anchor, ts15.SymbolFlags.Function)) {
|
|
3802
3968
|
if (symbol.name !== "hash")
|
|
3803
3969
|
continue;
|
|
3804
3970
|
const declaration = symbol.declarations?.[0];
|
|
@@ -3820,7 +3986,7 @@ function hashedSourceOfType(checker, type, canonical) {
|
|
|
3820
3986
|
return source?.isStringLiteral() ? source.value : undefined;
|
|
3821
3987
|
}
|
|
3822
3988
|
function staticAddressTextOf(checker, node, canonical) {
|
|
3823
|
-
return
|
|
3989
|
+
return ts15.isStringLiteralLike(node) ? node.text : hashedSourceOfType(checker, checker.getTypeAtLocation(node), canonical);
|
|
3824
3990
|
}
|
|
3825
3991
|
|
|
3826
3992
|
// src/url-cross-world-address.ts
|
|
@@ -3844,7 +4010,7 @@ function checkCrossWorldAddresses(input) {
|
|
|
3844
4010
|
if (worlds.length === 0)
|
|
3845
4011
|
continue;
|
|
3846
4012
|
const visit = (node) => {
|
|
3847
|
-
if (
|
|
4013
|
+
if (ts16.isExpression(node)) {
|
|
3848
4014
|
const entry = addressEntryOfArgument(checker, table, node);
|
|
3849
4015
|
if (entry !== undefined && slotRejectsForeignSocket(entry)) {
|
|
3850
4016
|
const address = staticAddressTextOf(checker, node, canonical);
|
|
@@ -3861,14 +4027,14 @@ function checkCrossWorldAddresses(input) {
|
|
|
3861
4027
|
}
|
|
3862
4028
|
}
|
|
3863
4029
|
}
|
|
3864
|
-
|
|
4030
|
+
ts16.forEachChild(node, visit);
|
|
3865
4031
|
};
|
|
3866
|
-
|
|
4032
|
+
ts16.forEachChild(sourceFile, visit);
|
|
3867
4033
|
}
|
|
3868
4034
|
return findings;
|
|
3869
4035
|
}
|
|
3870
4036
|
// src/url-fragment-reachability.ts
|
|
3871
|
-
import * as
|
|
4037
|
+
import * as ts17 from "typescript";
|
|
3872
4038
|
function pathOfKey(key) {
|
|
3873
4039
|
const socket = socketOfAddress(key);
|
|
3874
4040
|
return socket === undefined ? key : key.slice(socket.length + 1);
|
|
@@ -3913,7 +4079,7 @@ function checkUrlFragmentReachability(input) {
|
|
|
3913
4079
|
if (isAmbient2(sourceFile.fileName))
|
|
3914
4080
|
continue;
|
|
3915
4081
|
const visit = (node) => {
|
|
3916
|
-
if (
|
|
4082
|
+
if (ts17.isExpression(node) && isAddressClass(addressClassOfArgument(checker, table, node))) {
|
|
3917
4083
|
const text = staticAddressTextOf(checker, node, canonical);
|
|
3918
4084
|
if (text !== undefined) {
|
|
3919
4085
|
const hash = text.indexOf("#");
|
|
@@ -3932,9 +4098,9 @@ function checkUrlFragmentReachability(input) {
|
|
|
3932
4098
|
}
|
|
3933
4099
|
}
|
|
3934
4100
|
}
|
|
3935
|
-
|
|
4101
|
+
ts17.forEachChild(node, visit);
|
|
3936
4102
|
};
|
|
3937
|
-
|
|
4103
|
+
ts17.forEachChild(sourceFile, visit);
|
|
3938
4104
|
}
|
|
3939
4105
|
return { kind: "checked", findings };
|
|
3940
4106
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defold-typescript/transpiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "TypeScript-to-Lua build pipeline tuned for Defold's runtime.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"test": "bun test"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@defold-typescript/types": "0.
|
|
38
|
+
"@defold-typescript/types": "0.40.0",
|
|
39
39
|
"@typescript-to-lua/language-extensions": "1.19.0",
|
|
40
40
|
"typescript-to-lua": "^1.36.0"
|
|
41
41
|
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import {
|
|
3
|
+
createBinaryExpression,
|
|
4
|
+
createNumericLiteral,
|
|
5
|
+
type Expression,
|
|
6
|
+
getOriginalPos,
|
|
7
|
+
isCallExpression,
|
|
8
|
+
isNumericLiteral,
|
|
9
|
+
isStringLiteral,
|
|
10
|
+
isTableExpression,
|
|
11
|
+
type Plugin,
|
|
12
|
+
SyntaxKind,
|
|
13
|
+
setNodePosition,
|
|
14
|
+
type TableExpression,
|
|
15
|
+
} from "typescript-to-lua";
|
|
16
|
+
|
|
17
|
+
// The Defold functions whose `options.index` upstream documents as "index into
|
|
18
|
+
// array property (1 based)". TypeScript authors it zero-based; the lowering
|
|
19
|
+
// adds the one Lua expects.
|
|
20
|
+
export const ONE_BASED_INDEX_OPTION_APIS: ReadonlySet<string> = new Set([
|
|
21
|
+
"go.get",
|
|
22
|
+
"go.set",
|
|
23
|
+
"gui.get",
|
|
24
|
+
"gui.set",
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
export const INDEX_OPTION_VARIABLE_MESSAGE =
|
|
28
|
+
"`options.index` is zero-based and is converted to Defold's 1-based index only in an object literal written at the call; pass the options inline.";
|
|
29
|
+
export const INDEX_OPTION_SPREAD_MESSAGE =
|
|
30
|
+
"A spread carrying `index` cannot be converted to Defold's 1-based index; write `index` explicitly after the spread.";
|
|
31
|
+
export const INDEX_OPTION_UNDEFINED_MESSAGE =
|
|
32
|
+
"`options.index` may be undefined here, so it cannot be converted to Defold's 1-based index; pass a number, or leave `index` out.";
|
|
33
|
+
|
|
34
|
+
const OPTIONS_PARAMETER = "options";
|
|
35
|
+
const INDEX_KEY = "index";
|
|
36
|
+
|
|
37
|
+
const loweredLiterals = new WeakSet<ts.Node>();
|
|
38
|
+
|
|
39
|
+
// Keyed like `tableKey` in url-address-slots.ts: only a symbol declared in an
|
|
40
|
+
// ambient `declare global` block carries the `global.` prefix, so a project's
|
|
41
|
+
// own `go` or `gui` never matches. Walking up from the signature declaration
|
|
42
|
+
// reaches `go.get<P>()` from the function type its curried inner call resolves to.
|
|
43
|
+
function oneBasedApi(declaration: ts.Declaration, checker: ts.TypeChecker): boolean {
|
|
44
|
+
let node: ts.Node | undefined = declaration;
|
|
45
|
+
while (node !== undefined && !ts.isFunctionDeclaration(node)) node = node.parent;
|
|
46
|
+
if (node?.name === undefined) return false;
|
|
47
|
+
const symbol = checker.getSymbolAtLocation(node.name);
|
|
48
|
+
if (symbol === undefined) return false;
|
|
49
|
+
const fqn = checker.getFullyQualifiedName(symbol);
|
|
50
|
+
return fqn.startsWith("global.") && ONE_BASED_INDEX_OPTION_APIS.has(fqn.slice("global.".length));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function optionsArgument(
|
|
54
|
+
call: ts.CallExpression,
|
|
55
|
+
checker: ts.TypeChecker,
|
|
56
|
+
): ts.Expression | undefined {
|
|
57
|
+
const declaration = checker.getResolvedSignature(call)?.declaration;
|
|
58
|
+
if (declaration === undefined || !ts.isFunctionLike(declaration)) return undefined;
|
|
59
|
+
if (!oneBasedApi(declaration, checker)) return undefined;
|
|
60
|
+
const index = declaration.parameters.findIndex(
|
|
61
|
+
(parameter) =>
|
|
62
|
+
parameter.dotDotDotToken === undefined &&
|
|
63
|
+
ts.isIdentifier(parameter.name) &&
|
|
64
|
+
parameter.name.text === OPTIONS_PARAMETER,
|
|
65
|
+
);
|
|
66
|
+
return index === -1 ? undefined : call.arguments[index];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function unwrap(expression: ts.Expression): ts.Expression {
|
|
70
|
+
let current = expression;
|
|
71
|
+
while (
|
|
72
|
+
ts.isParenthesizedExpression(current) ||
|
|
73
|
+
ts.isAsExpression(current) ||
|
|
74
|
+
ts.isSatisfiesExpression(current) ||
|
|
75
|
+
ts.isNonNullExpression(current) ||
|
|
76
|
+
ts.isTypeAssertionExpression(current)
|
|
77
|
+
) {
|
|
78
|
+
current = current.expression;
|
|
79
|
+
}
|
|
80
|
+
return current;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function someConstituent(type: ts.Type, predicate: (type: ts.Type) => boolean): boolean {
|
|
84
|
+
return type.isUnion() ? type.types.some(predicate) : predicate(type);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function carriesIndex(type: ts.Type): boolean {
|
|
88
|
+
return someConstituent(type, (t) => t.getProperty(INDEX_KEY) !== undefined);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function admitsUndefined(type: ts.Type): boolean {
|
|
92
|
+
return someConstituent(
|
|
93
|
+
type,
|
|
94
|
+
(t) => (t.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function propertyName(
|
|
99
|
+
property: ts.ObjectLiteralElementLike,
|
|
100
|
+
checker: ts.TypeChecker,
|
|
101
|
+
): string | undefined {
|
|
102
|
+
const name = property.name;
|
|
103
|
+
if (name === undefined) return undefined;
|
|
104
|
+
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
|
|
105
|
+
return name.text;
|
|
106
|
+
}
|
|
107
|
+
if (ts.isComputedPropertyName(name)) {
|
|
108
|
+
const type = checker.getTypeAtLocation(name.expression);
|
|
109
|
+
if (type.isStringLiteral()) return type.value;
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function indexValueType(
|
|
115
|
+
property: ts.ObjectLiteralElementLike,
|
|
116
|
+
checker: ts.TypeChecker,
|
|
117
|
+
): ts.Type | undefined {
|
|
118
|
+
if (ts.isPropertyAssignment(property)) return checker.getTypeAtLocation(property.initializer);
|
|
119
|
+
if (ts.isShorthandPropertyAssignment(property)) return checker.getTypeAtLocation(property.name);
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function rejection(node: ts.Node, messageText: string): ts.Diagnostic {
|
|
124
|
+
const file = node.getSourceFile();
|
|
125
|
+
const start = node.getStart(file);
|
|
126
|
+
return {
|
|
127
|
+
file,
|
|
128
|
+
start,
|
|
129
|
+
length: node.getEnd() - start,
|
|
130
|
+
category: ts.DiagnosticCategory.Error,
|
|
131
|
+
code: 0,
|
|
132
|
+
messageText,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The first reason `options` cannot be lowered, or `undefined` when it can.
|
|
137
|
+
function rejectOptions(options: ts.Expression, checker: ts.TypeChecker): ts.Diagnostic | undefined {
|
|
138
|
+
const literal = unwrap(options);
|
|
139
|
+
if (!ts.isObjectLiteralExpression(literal)) {
|
|
140
|
+
return carriesIndex(checker.getTypeAtLocation(literal))
|
|
141
|
+
? rejection(options, INDEX_OPTION_VARIABLE_MESSAGE)
|
|
142
|
+
: undefined;
|
|
143
|
+
}
|
|
144
|
+
let uncoveredSpread: ts.SpreadAssignment | undefined;
|
|
145
|
+
let explicitIndex: ts.ObjectLiteralElementLike | undefined;
|
|
146
|
+
for (const property of literal.properties) {
|
|
147
|
+
if (ts.isSpreadAssignment(property)) {
|
|
148
|
+
if (carriesIndex(checker.getTypeAtLocation(property.expression))) uncoveredSpread = property;
|
|
149
|
+
} else if (propertyName(property, checker) === INDEX_KEY) {
|
|
150
|
+
uncoveredSpread = undefined;
|
|
151
|
+
explicitIndex = property;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (uncoveredSpread !== undefined) return rejection(uncoveredSpread, INDEX_OPTION_SPREAD_MESSAGE);
|
|
155
|
+
const valueType = explicitIndex && indexValueType(explicitIndex, checker);
|
|
156
|
+
if (explicitIndex !== undefined && valueType !== undefined && admitsUndefined(valueType)) {
|
|
157
|
+
return rejection(explicitIndex, INDEX_OPTION_UNDEFINED_MESSAGE);
|
|
158
|
+
}
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function plusOne(value: Expression): Expression {
|
|
163
|
+
if (isNumericLiteral(value)) {
|
|
164
|
+
return setNodePosition(createNumericLiteral(value.value + 1), getOriginalPos(value));
|
|
165
|
+
}
|
|
166
|
+
return setNodePosition(
|
|
167
|
+
createBinaryExpression(value, createNumericLiteral(1), SyntaxKind.AdditionOperator),
|
|
168
|
+
getOriginalPos(value),
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// `rejectOptions` guarantees the last `index` field is the effective one: a
|
|
173
|
+
// spread carrying `index` after it is rejected. Every earlier one is shadowed
|
|
174
|
+
// and keeps its source value.
|
|
175
|
+
function offsetEffectiveIndexField(tables: readonly TableExpression[]): void {
|
|
176
|
+
for (const field of tables.flatMap((table) => table.fields).reverse()) {
|
|
177
|
+
if (field.key !== undefined && isStringLiteral(field.key) && field.key.value === INDEX_KEY) {
|
|
178
|
+
field.value = plusOne(field.value);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const arrayIndexOptionLoweringPlugin: Plugin = {
|
|
185
|
+
visitors: {
|
|
186
|
+
[ts.SyntaxKind.CallExpression]: (node, context): Expression => {
|
|
187
|
+
const options = optionsArgument(node, context.checker);
|
|
188
|
+
if (options !== undefined) {
|
|
189
|
+
const diagnostic = rejectOptions(options, context.checker);
|
|
190
|
+
if (diagnostic !== undefined) {
|
|
191
|
+
context.diagnostics.push(diagnostic);
|
|
192
|
+
} else {
|
|
193
|
+
const literal = unwrap(options);
|
|
194
|
+
if (ts.isObjectLiteralExpression(literal)) loweredLiterals.add(literal);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return context.superTransformExpression(node);
|
|
198
|
+
},
|
|
199
|
+
// A spread lowers the literal to `__TS__ObjectAssign({}, base, {index = …})`,
|
|
200
|
+
// so the table carrying `index` can sit one call deep.
|
|
201
|
+
[ts.SyntaxKind.ObjectLiteralExpression]: (node, context): Expression => {
|
|
202
|
+
const result = context.superTransformExpression(node);
|
|
203
|
+
if (!loweredLiterals.has(node)) return result;
|
|
204
|
+
if (isTableExpression(result)) {
|
|
205
|
+
offsetEffectiveIndexField([result]);
|
|
206
|
+
} else if (isCallExpression(result)) {
|
|
207
|
+
offsetEffectiveIndexField(result.params.filter(isTableExpression));
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as ts from "typescript";
|
|
2
2
|
import * as tstl from "typescript-to-lua";
|
|
3
|
+
import { arrayIndexOptionLoweringPlugin } from "./array-index-option-lowering";
|
|
3
4
|
import { lifecycleErasurePlugin } from "./lifecycle-erasure";
|
|
4
5
|
import { messageDispatchLoweringPlugin } from "./message-dispatch-lowering";
|
|
5
6
|
import { messageGuardLoweringPlugin } from "./message-guard-lowering";
|
|
@@ -14,6 +15,7 @@ const LUA_PLUGINS: tstl.Plugin[] = [
|
|
|
14
15
|
windowEventGuardLoweringPlugin,
|
|
15
16
|
messageDispatchLoweringPlugin,
|
|
16
17
|
timersLoweringPlugin,
|
|
18
|
+
arrayIndexOptionLoweringPlugin,
|
|
17
19
|
];
|
|
18
20
|
|
|
19
21
|
const noopWriteFile: ts.WriteFileCallback = () => {};
|
package/src/session.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import * as ts from "typescript";
|
|
5
5
|
import * as tstl from "typescript-to-lua";
|
|
6
|
+
import { arrayIndexOptionLoweringPlugin } from "./array-index-option-lowering";
|
|
6
7
|
import { type CompanionEmitter, createCompanionEmitPlugin } from "./companion-emit";
|
|
7
8
|
import { editorScriptErasurePlugin } from "./editor-script-erasure";
|
|
8
9
|
import { lifecycleErasurePlugin } from "./lifecycle-erasure";
|
|
@@ -43,6 +44,7 @@ function compilerOptions(companionEmit: CompanionEmitter): tstl.CompilerOptions
|
|
|
43
44
|
{ plugin: windowEventGuardLoweringPlugin },
|
|
44
45
|
{ plugin: messageDispatchLoweringPlugin },
|
|
45
46
|
{ plugin: timersLoweringPlugin },
|
|
47
|
+
{ plugin: arrayIndexOptionLoweringPlugin },
|
|
46
48
|
{ plugin: companionEmit.plugin },
|
|
47
49
|
],
|
|
48
50
|
};
|
package/src/transpile.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type * as ts from "typescript";
|
|
5
5
|
import * as tstl from "typescript-to-lua";
|
|
6
|
+
import { arrayIndexOptionLoweringPlugin } from "./array-index-option-lowering";
|
|
6
7
|
import { createCompanionEmitPlugin } from "./companion-emit";
|
|
7
8
|
import { editorScriptErasurePlugin } from "./editor-script-erasure";
|
|
8
9
|
import {
|
|
@@ -321,6 +322,7 @@ export function transpileProject(input: TranspileProjectInput): TranspileProject
|
|
|
321
322
|
{ plugin: windowEventGuardLoweringPlugin },
|
|
322
323
|
{ plugin: messageDispatchLoweringPlugin },
|
|
323
324
|
{ plugin: timersLoweringPlugin },
|
|
325
|
+
{ plugin: arrayIndexOptionLoweringPlugin },
|
|
324
326
|
{ plugin: companionEmit.plugin },
|
|
325
327
|
],
|
|
326
328
|
});
|