typescript-to-lua 1.7.0 → 1.8.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/LuaLib.d.ts CHANGED
@@ -42,6 +42,7 @@ export declare enum LuaLibFeature {
42
42
  InstanceOf = "InstanceOf",
43
43
  InstanceOfObject = "InstanceOfObject",
44
44
  Iterator = "Iterator",
45
+ LuaIteratorSpread = "LuaIteratorSpread",
45
46
  Map = "Map",
46
47
  MathAtan2 = "MathAtan2",
47
48
  MathSign = "MathSign",
package/dist/LuaLib.js CHANGED
@@ -46,6 +46,7 @@ var LuaLibFeature;
46
46
  LuaLibFeature["InstanceOf"] = "InstanceOf";
47
47
  LuaLibFeature["InstanceOfObject"] = "InstanceOfObject";
48
48
  LuaLibFeature["Iterator"] = "Iterator";
49
+ LuaLibFeature["LuaIteratorSpread"] = "LuaIteratorSpread";
49
50
  LuaLibFeature["Map"] = "Map";
50
51
  LuaLibFeature["MathAtan2"] = "MathAtan2";
51
52
  LuaLibFeature["MathSign"] = "MathSign";
@@ -0,0 +1,9 @@
1
+ local function __TS__LuaIteratorSpread(self, state, firstKey)
2
+ local results = {}
3
+ local key, value = self(state, firstKey)
4
+ while key do
5
+ results[#results + 1] = {key, value}
6
+ key, value = self(state, key)
7
+ end
8
+ return __TS__Unpack(results)
9
+ end
@@ -1207,6 +1207,16 @@ local function __TS__InstanceOfObject(value)
1207
1207
  return valueType == "table" or valueType == "function"
1208
1208
  end
1209
1209
 
1210
+ local function __TS__LuaIteratorSpread(self, state, firstKey)
1211
+ local results = {}
1212
+ local key, value = self(state, firstKey)
1213
+ while key do
1214
+ results[#results + 1] = {key, value}
1215
+ key, value = self(state, key)
1216
+ end
1217
+ return __TS__Unpack(results)
1218
+ end
1219
+
1210
1220
  local Map
1211
1221
  do
1212
1222
  Map = __TS__Class()
@@ -2448,6 +2458,7 @@ return {
2448
2458
  __TS__InstanceOf = __TS__InstanceOf,
2449
2459
  __TS__InstanceOfObject = __TS__InstanceOfObject,
2450
2460
  __TS__Iterator = __TS__Iterator,
2461
+ __TS__LuaIteratorSpread = __TS__LuaIteratorSpread,
2451
2462
  Map = Map,
2452
2463
  __TS__MathAtan2 = __TS__MathAtan2,
2453
2464
  __TS__MathSign = __TS__MathSign,
@@ -264,6 +264,14 @@
264
264
  "Symbol"
265
265
  ]
266
266
  },
267
+ "LuaIteratorSpread": {
268
+ "exports": [
269
+ "__TS__LuaIteratorSpread"
270
+ ],
271
+ "dependencies": [
272
+ "Unpack"
273
+ ]
274
+ },
267
275
  "Map": {
268
276
  "exports": [
269
277
  "Map"
@@ -60,5 +60,6 @@ export declare enum IterableExtensionKind {
60
60
  Pairs = "Pairs",
61
61
  PairsKey = "PairsKey"
62
62
  }
63
+ export declare function isLuaIterable(context: TransformationContext, type: ts.Type): boolean;
63
64
  export declare function getIterableExtensionTypeForType(context: TransformationContext, type: ts.Type): IterableExtensionKind | undefined;
64
65
  export declare function getIterableExtensionKindForNode(context: TransformationContext, node: ts.Node): IterableExtensionKind | undefined;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getIterableExtensionKindForNode = exports.getIterableExtensionTypeForType = exports.IterableExtensionKind = exports.getExtensionKindForSymbol = exports.getExtensionKindForNode = exports.getExtensionKindForType = exports.ExtensionKind = void 0;
3
+ exports.getIterableExtensionKindForNode = exports.getIterableExtensionTypeForType = exports.isLuaIterable = exports.IterableExtensionKind = exports.getExtensionKindForSymbol = exports.getExtensionKindForNode = exports.getExtensionKindForType = exports.ExtensionKind = void 0;
4
4
  const ts = require("typescript");
5
5
  var ExtensionKind;
6
6
  (function (ExtensionKind) {
@@ -96,6 +96,10 @@ var IterableExtensionKind;
96
96
  IterableExtensionKind["Pairs"] = "Pairs";
97
97
  IterableExtensionKind["PairsKey"] = "PairsKey";
98
98
  })(IterableExtensionKind = exports.IterableExtensionKind || (exports.IterableExtensionKind = {}));
99
+ function isLuaIterable(context, type) {
100
+ return getPropertyValue(context, type, "__tstlIterable") !== undefined;
101
+ }
102
+ exports.isLuaIterable = isLuaIterable;
99
103
  function getIterableExtensionTypeForType(context, type) {
100
104
  const value = getPropertyValue(context, type, "__tstlIterable");
101
105
  if (value && value in IterableExtensionKind) {
@@ -117,7 +117,7 @@ function transformPropertyCall(context, node, calledMethod) {
117
117
  if (calledMethod.expression.kind === ts.SyntaxKind.SuperKeyword) {
118
118
  // Super calls take the format of super.call(self,...)
119
119
  const parameters = transformArguments(context, node.arguments, signature, ts.factory.createThis());
120
- return lua.createCallExpression(context.transformExpression(node.expression), parameters);
120
+ return lua.createCallExpression(context.transformExpression(node.expression), parameters, node);
121
121
  }
122
122
  const signatureDeclaration = signature === null || signature === void 0 ? void 0 : signature.getDeclaration();
123
123
  if (!signatureDeclaration || (0, function_context_1.getDeclarationContextType)(context, signatureDeclaration) !== function_context_1.ContextType.Void) {
@@ -140,7 +140,7 @@ function transformElementCall(context, node) {
140
140
  else {
141
141
  // No context
142
142
  const [expression, parameters] = transformCallAndArguments(context, node.expression, node.arguments, signature);
143
- return lua.createCallExpression(expression, parameters);
143
+ return lua.createCallExpression(expression, parameters, node);
144
144
  }
145
145
  }
146
146
  const transformCallExpression = (node, context) => {
@@ -175,7 +175,7 @@ const transformCallExpression = (node, context) => {
175
175
  // Handle super calls properly
176
176
  if (calledExpression.kind === ts.SyntaxKind.SuperKeyword) {
177
177
  const parameters = transformArguments(context, node.arguments, signature, ts.factory.createThis());
178
- return lua.createCallExpression(lua.createTableIndexExpression(context.transformExpression(ts.factory.createSuper()), lua.createStringLiteral("____constructor")), parameters);
178
+ return lua.createCallExpression(lua.createTableIndexExpression(context.transformExpression(ts.factory.createSuper()), lua.createStringLiteral("____constructor")), parameters, node);
179
179
  }
180
180
  let callPath;
181
181
  let parameters;
@@ -9,6 +9,7 @@ const diagnostics_1 = require("../../utils/diagnostics");
9
9
  const utils_2 = require("../../../utils");
10
10
  const multi_1 = require("./multi");
11
11
  function transformForOfMultiIterableStatement(context, statement, block, luaIterator, invalidMultiUseDiagnostic) {
12
+ context.pushPrecedingStatements();
12
13
  let identifiers = [];
13
14
  if (ts.isVariableDeclarationList(statement.initializer)) {
14
15
  // Variables declared in for loop
@@ -36,6 +37,7 @@ function transformForOfMultiIterableStatement(context, statement, block, luaIter
36
37
  if (identifiers.length === 0) {
37
38
  identifiers.push(lua.createAnonymousIdentifier());
38
39
  }
40
+ block.statements.unshift(...context.popPrecedingStatements());
39
41
  return lua.createForInStatement(block, identifiers, [luaIterator], statement);
40
42
  }
41
43
  function transformForOfIterableStatement(context, statement, block) {
@@ -117,8 +117,8 @@ const transformImportEqualsDeclaration = (node, context) => {
117
117
  exports.transformImportEqualsDeclaration = transformImportEqualsDeclaration;
118
118
  const transformImportExpression = (node, context) => {
119
119
  (0, lualib_1.importLuaLibFeature)(context, lualib_1.LuaLibFeature.Promise);
120
- const importPath = node.arguments.map(a => context.transformExpression(a));
121
- return lua.createCallExpression((0, promise_1.createStaticPromiseFunctionAccessor)("resolve", node), importPath, node);
120
+ const moduleRequire = node.arguments.length > 0 ? createModuleRequire(context, node.arguments[0], node) : lua.createNilLiteral();
121
+ return lua.createCallExpression((0, promise_1.createStaticPromiseFunctionAccessor)("resolve", node), [moduleRequire], node);
122
122
  };
123
123
  exports.transformImportExpression = transformImportExpression;
124
124
  //# sourceMappingURL=import.js.map
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transformSpreadElement = exports.isOptimizedVarArgSpread = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../LuaAST");
6
+ const language_extensions_1 = require("../utils/language-extensions");
6
7
  const lua_ast_1 = require("../utils/lua-ast");
7
8
  const lualib_1 = require("../utils/lualib");
8
9
  const scope_1 = require("../utils/scope");
@@ -60,6 +61,9 @@ const transformSpreadElement = (node, context) => {
60
61
  if ((0, multi_1.isMultiReturnCall)(context, tsInnerExpression))
61
62
  return innerExpression;
62
63
  const type = context.checker.getTypeAtLocation(node.expression); // not ts-inner expression, in case of casts
64
+ if ((0, language_extensions_1.isLuaIterable)(context, type)) {
65
+ return (0, lualib_1.transformLuaLibFunction)(context, lualib_1.LuaLibFeature.LuaIteratorSpread, node, innerExpression);
66
+ }
63
67
  if ((0, typescript_1.isArrayType)(context, type)) {
64
68
  return (0, lua_ast_1.createUnpackCall)(context, innerExpression, node);
65
69
  }
@@ -104,26 +104,33 @@ class ResolutionContext {
104
104
  if (this.options.tstlVerbose) {
105
105
  console.log(`Resolving "${dependency}" from ${(0, utils_1.normalizeSlashes)(requiringFile.fileName)}`);
106
106
  }
107
+ const requiredFromLuaFile = requiringFile.fileName.endsWith(".lua");
108
+ const dependencyPath = requiredFromLuaFile ? luaRequireToPath(dependency) : dependency;
109
+ if (requiredFromLuaFile && isNodeModulesFile(requiringFile.fileName)) {
110
+ // If requiring file is in lua module, try to resolve sibling in that file first
111
+ const resolvedNodeModulesFile = this.resolveLuaDependencyPathFromNodeModules(requiringFile, dependencyPath);
112
+ if (resolvedNodeModulesFile)
113
+ return resolvedNodeModulesFile;
114
+ }
107
115
  // Check if the import is relative
108
116
  const isRelative = ["/", "./", "../"].some(p => dependency.startsWith(p));
109
117
  // If the import is relative, always resolve it relative to the requiring file
110
118
  // If the import is not relative, resolve it relative to options.baseUrl if it is set
111
119
  const relativeTo = isRelative ? fileDirectory : (_a = this.options.baseUrl) !== null && _a !== void 0 ? _a : fileDirectory;
112
120
  // Check if file is a file in the project
113
- const resolvedPath = path.join(relativeTo, dependency);
121
+ const resolvedPath = path.join(relativeTo, dependencyPath);
114
122
  const fileFromPath = this.getFileFromPath(resolvedPath);
115
123
  if (fileFromPath)
116
124
  return fileFromPath;
117
- // Check if this is a sibling of a required lua file
118
- if (requiringFile.fileName.endsWith(".lua")) {
119
- const luaFilePath = resolveLuaPath(fileDirectory, dependency, this.emitHost);
120
- if (luaFilePath) {
121
- return luaFilePath;
122
- }
125
+ if (this.options.paths && this.options.baseUrl) {
126
+ // If no file found yet and paths are present, try to find project file via paths mappings
127
+ const fileFromPaths = this.tryGetModuleNameFromPaths(dependencyPath, this.options.paths, this.options.baseUrl);
128
+ if (fileFromPaths)
129
+ return fileFromPaths;
123
130
  }
124
131
  // Not a TS file in our project sources, use resolver to check if we can find dependency
125
132
  try {
126
- const resolveResult = resolver.resolveSync({}, fileDirectory, dependency);
133
+ const resolveResult = resolver.resolveSync({}, fileDirectory, dependencyPath);
127
134
  if (resolveResult)
128
135
  return resolveResult;
129
136
  }
@@ -132,6 +139,25 @@ class ResolutionContext {
132
139
  }
133
140
  return undefined;
134
141
  }
142
+ resolveLuaDependencyPathFromNodeModules(requiringFile, dependency) {
143
+ // We don't know for sure where the lua root is, so guess it is at package root
144
+ const splitPath = path.normalize(requiringFile.fileName).split(path.sep);
145
+ let packageRootIndex = splitPath.lastIndexOf("node_modules") + 2;
146
+ let packageRoot = splitPath.slice(0, packageRootIndex).join(path.sep);
147
+ while (packageRootIndex < splitPath.length) {
148
+ // Try to find lua file relative to currently guessed Lua root
149
+ const resolvedPath = path.join(packageRoot, dependency);
150
+ const fileFromPath = this.getFileFromPath(resolvedPath);
151
+ if (fileFromPath) {
152
+ return fileFromPath;
153
+ }
154
+ else {
155
+ // Did not find file at current root, try again one directory deeper
156
+ packageRoot = path.join(packageRoot, splitPath[packageRootIndex++]);
157
+ }
158
+ }
159
+ return undefined;
160
+ }
135
161
  getFileFromPath(resolvedPath) {
136
162
  const existingFile = this.pathToFile.get(resolvedPath);
137
163
  if (existingFile)
@@ -167,6 +193,40 @@ class ResolutionContext {
167
193
  }
168
194
  }
169
195
  }
196
+ // Taken from TS and modified: https://github.com/microsoft/TypeScript/blob/88a1e3a1dd8d2d86e844ff1c16d5f041cebcfdb9/src/compiler/moduleSpecifiers.ts#L562
197
+ tryGetModuleNameFromPaths(relativeToBaseUrl, paths, baseUrl) {
198
+ const relativeImport = removeTrailingDirectorySeparator((0, utils_1.normalizeSlashes)(relativeToBaseUrl));
199
+ for (const [importPattern, targetPatterns] of Object.entries(paths)) {
200
+ const pattern = removeFileExtension((0, utils_1.normalizeSlashes)(importPattern));
201
+ const indexOfStar = pattern.indexOf("*");
202
+ if (indexOfStar !== -1) {
203
+ // Try to match <prefix>*<suffix> to relativeImport
204
+ const prefix = pattern.substring(0, indexOfStar);
205
+ const suffix = pattern.substring(indexOfStar + 1);
206
+ if ((relativeImport.length >= prefix.length + suffix.length &&
207
+ relativeImport.startsWith(prefix) &&
208
+ relativeImport.endsWith(suffix)) ||
209
+ (!suffix && relativeImport === removeTrailingDirectorySeparator(prefix))) {
210
+ // If import matches <prefix>*<suffix>, extract the matched * path
211
+ const matchedStar = relativeImport.substring(prefix.length, relativeImport.length - suffix.length);
212
+ // Try to resolve to the target patterns with filled in * pattern
213
+ for (const target of targetPatterns) {
214
+ const file = this.getFileFromPath(path.join(baseUrl, target.replace("*", matchedStar)));
215
+ if (file)
216
+ return file;
217
+ }
218
+ }
219
+ }
220
+ else if (pattern === relativeImport) {
221
+ // If there is no * pattern, check for exact matches and try those targets
222
+ for (const target of targetPatterns) {
223
+ const file = this.getFileFromPath(path.join(baseUrl, target));
224
+ if (file)
225
+ return file;
226
+ }
227
+ }
228
+ }
229
+ }
170
230
  }
171
231
  function resolveDependencies(program, files, emitHost) {
172
232
  const options = program.getCompilerOptions();
@@ -181,34 +241,6 @@ function resolveDependencies(program, files, emitHost) {
181
241
  return { resolvedFiles: [...resolutionContext.resolvedFiles.values()], diagnostics: resolutionContext.diagnostics };
182
242
  }
183
243
  exports.resolveDependencies = resolveDependencies;
184
- function resolveLuaPath(fromPath, dependency, emitHost) {
185
- const splitDependency = dependency.split(".");
186
- if (splitDependency.length === 1) {
187
- // If dependency has just one part (the file), look for a lua file with that name
188
- const fileDirectory = walkUpFileTreeUntil(fromPath, dir => emitHost.fileExists(path.join(dir, dependency) + ".lua"));
189
- if (fileDirectory) {
190
- return path.join(fileDirectory, dependency) + ".lua";
191
- }
192
- }
193
- else {
194
- // If dependency has multiple parts, look for the first directory of the require path, which must be in the lua root
195
- const luaRoot = walkUpFileTreeUntil(fromPath, dir => emitHost.directoryExists(path.join(dir, splitDependency[0])));
196
- if (luaRoot) {
197
- return path.join(luaRoot, dependency.replace(/\./g, path.sep)) + ".lua";
198
- }
199
- }
200
- }
201
- function walkUpFileTreeUntil(fromDirectory, predicate) {
202
- const currentDir = path.normalize(fromDirectory).split(path.sep);
203
- while (currentDir.length > 0) {
204
- const dir = currentDir.join(path.sep);
205
- if (predicate(dir)) {
206
- return dir;
207
- }
208
- currentDir.pop();
209
- }
210
- return undefined;
211
- }
212
244
  function shouldRewriteRequires(resolvedDependency, program) {
213
245
  return !isBuildModeLibrary(program) || !isNodeModulesFile(resolvedDependency);
214
246
  }
@@ -228,7 +260,7 @@ function isBuildModeLibrary(program) {
228
260
  function findRequiredPaths(code) {
229
261
  // Find all require("<path>") paths in a lua code string
230
262
  const paths = [];
231
- const pattern = /(^|\s|;|=)require\("(.+?)"\)/g;
263
+ const pattern = /(^|\s|;|=|\()require\("(.+?)"\)/g;
232
264
  // eslint-disable-next-line @typescript-eslint/ban-types
233
265
  let match;
234
266
  while ((match = pattern.exec(code))) {
@@ -240,7 +272,7 @@ function replaceRequireInCode(file, originalRequire, newRequire) {
240
272
  const requirePath = (0, utils_1.formatPathToLuaPath)(newRequire.replace(".lua", ""));
241
273
  // Escape special characters to prevent the regex from breaking...
242
274
  const escapedRequire = originalRequire.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
243
- file.code = file.code.replace(new RegExp(`(^|\\s|;|=)require\\("${escapedRequire}"\\)`), `$1require("${requirePath}")`);
275
+ file.code = file.code.replace(new RegExp(`(^|\\s|;|=|\\()require\\("${escapedRequire}"\\)`), `$1require("${requirePath}")`);
244
276
  }
245
277
  function replaceRequireInSourceMap(file, originalRequire, newRequire) {
246
278
  const requirePath = (0, utils_1.formatPathToLuaPath)(newRequire.replace(".lua", ""));
@@ -285,4 +317,13 @@ function fallbackResolve(required, sourceRootDir, fileDir) {
285
317
  .filter(s => s !== "." && s !== "..")
286
318
  .join(path.sep));
287
319
  }
320
+ function luaRequireToPath(requirePath) {
321
+ return requirePath.replace(/\./g, path.sep);
322
+ }
323
+ function removeFileExtension(path) {
324
+ return path.includes(".") ? (0, utils_1.trimExtension)(path) : path;
325
+ }
326
+ function removeTrailingDirectorySeparator(path) {
327
+ return path.endsWith("/") || path.endsWith("\\") ? path.substring(0, -1) : path;
328
+ }
288
329
  //# sourceMappingURL=resolve.js.map
@@ -630,7 +630,7 @@ declare const LuaMap: (new <K extends AnyNotNil = AnyNotNil, V = any>() => LuaMa
630
630
  * @param K The type of the keys used to access the table.
631
631
  * @param V The type of the values stored in the table.
632
632
  */
633
- declare interface LuaReadonlyMap<K extends AnyNotNil = AnyNotNil, V = any> extends LuaPairsIterable<K, V> {
633
+ declare interface ReadonlyLuaMap<K extends AnyNotNil = AnyNotNil, V = any> extends LuaPairsIterable<K, V> {
634
634
  get: LuaTableGetMethod<K, V>;
635
635
  has: LuaTableHasMethod<K>;
636
636
  }
@@ -660,6 +660,17 @@ declare const LuaSet: (new <T extends AnyNotNil = AnyNotNil>() => LuaSet<T>) & L
660
660
  *
661
661
  * @param T The type of the keys used to access the table.
662
662
  */
663
- declare interface LuaReadonlySet<T extends AnyNotNil = AnyNotNil> extends LuaPairsKeyIterable<T> {
663
+ declare interface ReadonlyLuaSet<T extends AnyNotNil = AnyNotNil> extends LuaPairsKeyIterable<T> {
664
664
  has: LuaTableHasMethod<T>;
665
665
  }
666
+
667
+ interface ObjectConstructor {
668
+ /** Returns an array of keys of an object, when iterated with `pairs`. */
669
+ keys<K>(o: LuaPairsIterable<K, any> | LuaPairsKeyIterable<K>): K[];
670
+
671
+ /** Returns an array of values of an object, when iterated with `pairs`. */
672
+ values<V>(o: LuaPairsIterable<any, V>): V[];
673
+
674
+ /** Returns an array of key/values of an object, when iterated with `pairs`. */
675
+ entries<K, V>(o: LuaPairsIterable<K, V>): Array<[K, V]>;
676
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typescript-to-lua",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "A generic TypeScript to Lua transpiler. Write your code in TypeScript and publish Lua!",
5
5
  "repository": "https://github.com/TypeScriptToLua/TypeScriptToLua",
6
6
  "homepage": "https://typescripttolua.github.io/",