typescript-to-lua 1.6.3 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/CompilerOptions.d.ts +1 -0
  2. package/dist/cli/parse.js +5 -0
  3. package/dist/lualib/ArraySlice.lua +10 -2
  4. package/dist/lualib/ArraySplice.lua +5 -1
  5. package/dist/lualib/Generator.lua +8 -1
  6. package/dist/lualib/ParseFloat.lua +5 -1
  7. package/dist/lualib/SparseArraySpread.lua +5 -1
  8. package/dist/lualib/StringCharCodeAt.lua +5 -1
  9. package/dist/lualib/lualib_bundle.lua +38 -7
  10. package/dist/lualib-build/plugin.js +1 -2
  11. package/dist/measure-performance.d.ts +15 -0
  12. package/dist/measure-performance.js +83 -0
  13. package/dist/transformation/builtins/array.js +2 -1
  14. package/dist/transformation/builtins/function.js +1 -1
  15. package/dist/transformation/builtins/global.d.ts +1 -1
  16. package/dist/transformation/builtins/global.js +13 -12
  17. package/dist/transformation/builtins/index.d.ts +2 -2
  18. package/dist/transformation/builtins/index.js +87 -89
  19. package/dist/transformation/builtins/object.d.ts +1 -1
  20. package/dist/transformation/builtins/object.js +4 -4
  21. package/dist/transformation/builtins/promise.js +4 -2
  22. package/dist/transformation/builtins/string.js +2 -0
  23. package/dist/transformation/context/context.d.ts +15 -0
  24. package/dist/transformation/context/context.js +66 -24
  25. package/dist/transformation/context/visitors.d.ts +1 -1
  26. package/dist/transformation/index.js +6 -5
  27. package/dist/transformation/utils/annotations.d.ts +1 -17
  28. package/dist/transformation/utils/annotations.js +48 -88
  29. package/dist/transformation/utils/assignment-validation.js +27 -23
  30. package/dist/transformation/utils/diagnostics.d.ts +1 -4
  31. package/dist/transformation/utils/diagnostics.js +2 -3
  32. package/dist/transformation/utils/function-context.d.ts +1 -1
  33. package/dist/transformation/utils/function-context.js +57 -51
  34. package/dist/transformation/utils/language-extensions.d.ts +57 -51
  35. package/dist/transformation/utils/language-extensions.js +99 -119
  36. package/dist/transformation/utils/lualib.d.ts +0 -1
  37. package/dist/transformation/utils/lualib.js +2 -8
  38. package/dist/transformation/utils/safe-names.d.ts +1 -1
  39. package/dist/transformation/utils/safe-names.js +3 -6
  40. package/dist/transformation/utils/scope.d.ts +0 -2
  41. package/dist/transformation/utils/scope.js +10 -27
  42. package/dist/transformation/utils/symbols.d.ts +1 -1
  43. package/dist/transformation/utils/symbols.js +7 -20
  44. package/dist/transformation/utils/typescript/index.d.ts +1 -0
  45. package/dist/transformation/utils/typescript/index.js +2 -1
  46. package/dist/transformation/utils/typescript/types.d.ts +2 -5
  47. package/dist/transformation/utils/typescript/types.js +27 -37
  48. package/dist/transformation/visitors/access.js +1 -4
  49. package/dist/transformation/visitors/block.js +4 -4
  50. package/dist/transformation/visitors/call.js +10 -21
  51. package/dist/transformation/visitors/class/index.js +8 -20
  52. package/dist/transformation/visitors/class/members/constructor.js +2 -2
  53. package/dist/transformation/visitors/class/new.js +2 -5
  54. package/dist/transformation/visitors/class/setup.d.ts +1 -1
  55. package/dist/transformation/visitors/class/setup.js +4 -4
  56. package/dist/transformation/visitors/class/utils.d.ts +1 -1
  57. package/dist/transformation/visitors/class/utils.js +2 -9
  58. package/dist/transformation/visitors/conditional.js +4 -4
  59. package/dist/transformation/visitors/function.d.ts +1 -1
  60. package/dist/transformation/visitors/function.js +8 -16
  61. package/dist/transformation/visitors/identifier.d.ts +1 -0
  62. package/dist/transformation/visitors/identifier.js +40 -44
  63. package/dist/transformation/visitors/language-extensions/call-extension.d.ts +10 -0
  64. package/dist/transformation/visitors/language-extensions/call-extension.js +23 -0
  65. package/dist/transformation/visitors/language-extensions/identifier.d.ts +5 -0
  66. package/dist/transformation/visitors/language-extensions/identifier.js +27 -0
  67. package/dist/transformation/visitors/language-extensions/iterable.d.ts +2 -3
  68. package/dist/transformation/visitors/language-extensions/iterable.js +23 -22
  69. package/dist/transformation/visitors/language-extensions/multi.d.ts +0 -1
  70. package/dist/transformation/visitors/language-extensions/multi.js +10 -24
  71. package/dist/transformation/visitors/language-extensions/operators.d.ts +2 -5
  72. package/dist/transformation/visitors/language-extensions/operators.js +89 -96
  73. package/dist/transformation/visitors/language-extensions/range.js +4 -2
  74. package/dist/transformation/visitors/language-extensions/table.d.ts +6 -3
  75. package/dist/transformation/visitors/language-extensions/table.js +46 -75
  76. package/dist/transformation/visitors/language-extensions/vararg.js +7 -3
  77. package/dist/transformation/visitors/literal.js +2 -23
  78. package/dist/transformation/visitors/loops/for-of.js +18 -18
  79. package/dist/transformation/visitors/loops/utils.js +5 -5
  80. package/dist/transformation/visitors/modules/import.js +2 -2
  81. package/dist/transformation/visitors/namespace.js +5 -14
  82. package/dist/transformation/visitors/return.js +2 -3
  83. package/dist/transformation/visitors/sourceFile.js +3 -4
  84. package/dist/transformation/visitors/spread.js +0 -5
  85. package/dist/transformation/visitors/switch.js +2 -2
  86. package/dist/transformation/visitors/variable-declaration.js +1 -1
  87. package/dist/transpilation/plugins.js +3 -0
  88. package/dist/transpilation/resolve.js +210 -186
  89. package/dist/transpilation/transpile.js +10 -0
  90. package/dist/transpilation/transpiler.d.ts +1 -0
  91. package/dist/transpilation/transpiler.js +18 -5
  92. package/dist/tstl.js +21 -0
  93. package/language-extensions/index.d.ts +167 -67
  94. package/package.json +2 -2
  95. package/dist/transformation/visitors/language-extensions/index.d.ts +0 -4
  96. package/dist/transformation/visitors/language-extensions/index.js +0 -17
  97. package/dist/transformation/visitors/language-extensions/pairsIterable.d.ts +0 -5
  98. package/dist/transformation/visitors/language-extensions/pairsIterable.js +0 -53
@@ -3,8 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transformModuleDeclaration = exports.createModuleLocalNameIdentifier = void 0;
4
4
  const ts = require("typescript");
5
5
  const lua = require("../../LuaAST");
6
- const annotations_1 = require("../utils/annotations");
7
- const diagnostics_1 = require("../utils/diagnostics");
8
6
  const export_1 = require("../utils/export");
9
7
  const lua_ast_1 = require("../utils/lua-ast");
10
8
  const safe_names_1 = require("../utils/safe-names");
@@ -33,16 +31,9 @@ function moduleHasEmittedBody(node) {
33
31
  }
34
32
  return false;
35
33
  }
36
- // Static context -> namespace dictionary keeping the current namespace for each transformation context
37
- const currentNamespaces = new WeakMap();
38
34
  const transformModuleDeclaration = (node, context) => {
39
35
  var _a, _b;
40
- const annotations = (0, annotations_1.getTypeAnnotations)(context.checker.getTypeAtLocation(node));
41
- // If phantom namespace elide the declaration and return the body
42
- if (annotations.has(annotations_1.AnnotationKind.Phantom)) {
43
- context.diagnostics.push((0, diagnostics_1.annotationRemoved)(node, annotations_1.AnnotationKind.Phantom));
44
- }
45
- const currentNamespace = currentNamespaces.get(context);
36
+ const currentNamespace = context.currentNamespaces;
46
37
  const result = [];
47
38
  const symbol = context.checker.getSymbolAtLocation(node.name);
48
39
  const hasExports = symbol !== undefined && context.checker.getExportsOfModule(symbol).length > 0;
@@ -75,15 +66,15 @@ const transformModuleDeclaration = (node, context) => {
75
66
  }
76
67
  // Set current namespace for nested NS
77
68
  // Keep previous namespace to reset after block transpilation
78
- currentNamespaces.set(context, node);
69
+ context.currentNamespaces = node;
79
70
  // Transform moduleblock to block and visit it
80
71
  if (moduleHasEmittedBody(node)) {
81
- (0, scope_1.pushScope)(context, scope_1.ScopeType.Block);
72
+ context.pushScope(scope_1.ScopeType.Block);
82
73
  const statements = (0, scope_1.performHoisting)(context, context.transformStatements(ts.isModuleBlock(node.body) ? node.body.statements : node.body));
83
- (0, scope_1.popScope)(context);
74
+ context.popScope();
84
75
  result.push(lua.createDoStatement(statements));
85
76
  }
86
- currentNamespaces.set(context, currentNamespace);
77
+ context.currentNamespaces = currentNamespace;
87
78
  return result;
88
79
  };
89
80
  exports.transformModuleDeclaration = transformModuleDeclaration;
@@ -60,7 +60,6 @@ const transformReturnStatement = (statement, context) => {
60
60
  };
61
61
  exports.transformReturnStatement = transformReturnStatement;
62
62
  function createReturnStatement(context, values, node) {
63
- const results = [...values];
64
63
  if ((0, typescript_1.isInAsyncFunction)(node)) {
65
64
  return lua.createReturnStatement([
66
65
  lua.createCallExpression(lua.createIdentifier("____awaiter_resolve"), [lua.createNilLiteral(), ...values]),
@@ -68,9 +67,9 @@ function createReturnStatement(context, values, node) {
68
67
  }
69
68
  if (isInTryCatch(context)) {
70
69
  // Bubble up explicit return flag and check if we're inside a try/catch block
71
- results.unshift(lua.createBooleanLiteral(true));
70
+ values = [lua.createBooleanLiteral(true), ...values];
72
71
  }
73
- return lua.createReturnStatement(results, node);
72
+ return lua.createReturnStatement(values, node);
74
73
  }
75
74
  exports.createReturnStatement = createReturnStatement;
76
75
  function isInTryCatch(context) {
@@ -5,7 +5,6 @@ const ts = require("typescript");
5
5
  const lua = require("../../LuaAST");
6
6
  const utils_1 = require("../../utils");
7
7
  const lua_ast_1 = require("../utils/lua-ast");
8
- const lualib_1 = require("../utils/lualib");
9
8
  const preceding_statements_1 = require("../utils/preceding-statements");
10
9
  const scope_1 = require("../utils/scope");
11
10
  const typescript_1 = require("../utils/typescript");
@@ -28,9 +27,9 @@ const transformSourceFileNode = (node, context) => {
28
27
  }
29
28
  }
30
29
  else {
31
- (0, scope_1.pushScope)(context, scope_1.ScopeType.File);
30
+ context.pushScope(scope_1.ScopeType.File);
32
31
  statements = (0, scope_1.performHoisting)(context, context.transformStatements(node.statements));
33
- (0, scope_1.popScope)(context);
32
+ context.popScope();
34
33
  if (context.isModule) {
35
34
  // If export equals was not used. Create the exports table.
36
35
  // local ____exports = {}
@@ -42,7 +41,7 @@ const transformSourceFileNode = (node, context) => {
42
41
  }
43
42
  }
44
43
  const trivia = (_b = (_a = node.getFullText().match(/^#!.*\r?\n/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : "";
45
- return lua.createFile(statements, (0, lualib_1.getUsedLuaLibFeatures)(context), trivia, node);
44
+ return lua.createFile(statements, context.usedLuaLibFeatures, trivia, node);
46
45
  };
47
46
  exports.transformSourceFileNode = transformSourceFileNode;
48
47
  //# sourceMappingURL=sourceFile.js.map
@@ -3,13 +3,11 @@ 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 annotations_1 = require("../utils/annotations");
7
6
  const lua_ast_1 = require("../utils/lua-ast");
8
7
  const lualib_1 = require("../utils/lualib");
9
8
  const scope_1 = require("../utils/scope");
10
9
  const typescript_1 = require("../utils/typescript");
11
10
  const multi_1 = require("./language-extensions/multi");
12
- const diagnostics_1 = require("../utils/diagnostics");
13
11
  const vararg_1 = require("./language-extensions/vararg");
14
12
  function isOptimizedVarArgSpread(context, symbol, identifier) {
15
13
  var _a;
@@ -53,9 +51,6 @@ exports.isOptimizedVarArgSpread = isOptimizedVarArgSpread;
53
51
  const transformSpreadElement = (node, context) => {
54
52
  const tsInnerExpression = ts.skipOuterExpressions(node.expression);
55
53
  if (ts.isIdentifier(tsInnerExpression)) {
56
- if ((0, annotations_1.isVarargType)(context, tsInnerExpression)) {
57
- context.diagnostics.push((0, diagnostics_1.annotationRemoved)(node, annotations_1.AnnotationKind.Vararg));
58
- }
59
54
  const symbol = context.checker.getSymbolAtLocation(tsInnerExpression);
60
55
  if (symbol && isOptimizedVarArgSpread(context, symbol, tsInnerExpression)) {
61
56
  return lua.createDotsLiteral(node);
@@ -39,7 +39,7 @@ const coalesceCondition = (condition, conditionPrecedingStatements, switchVariab
39
39
  return [[...conditionPrecedingStatements, ...precedingStatements], comparison];
40
40
  };
41
41
  const transformSwitchStatement = (statement, context) => {
42
- const scope = (0, scope_1.pushScope)(context, scope_1.ScopeType.Switch);
42
+ const scope = context.pushScope(scope_1.ScopeType.Switch);
43
43
  // Give the switch and condition accumulator a unique name to prevent nested switches from acting up.
44
44
  const switchName = `____switch${scope.id}`;
45
45
  const conditionName = `____cond${scope.id}`;
@@ -158,7 +158,7 @@ const transformSwitchStatement = (statement, context) => {
158
158
  if (hoistedIdentifiers.length > 0) {
159
159
  statements.unshift(lua.createVariableDeclarationStatement(hoistedIdentifiers));
160
160
  }
161
- (0, scope_1.popScope)(context);
161
+ context.popScope();
162
162
  // Add the switch expression after hoisting
163
163
  const expression = context.transformExpression(statement.expression);
164
164
  statements.unshift(lua.createVariableDeclarationStatement(switchVariable, expression));
@@ -201,7 +201,7 @@ function transformVariableDeclaration(context, statement) {
201
201
  // Skip named function expressions because they will have been wrapped already
202
202
  if (ts.isFunctionExpression(initializer) && initializer.name)
203
203
  return false;
204
- return (0, function_1.isFunctionTypeWithProperties)(context.checker.getTypeAtLocation(statement.name));
204
+ return (0, function_1.isFunctionTypeWithProperties)(context, context.checker.getTypeAtLocation(statement.name));
205
205
  }
206
206
  }
207
207
  exports.transformVariableDeclaration = transformVariableDeclaration;
@@ -2,8 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getPlugins = void 0;
4
4
  const utils_1 = require("./utils");
5
+ const performance = require("../measure-performance");
5
6
  function getPlugins(program) {
6
7
  var _a;
8
+ performance.startSection("getPlugins");
7
9
  const diagnostics = [];
8
10
  const pluginsFromOptions = [];
9
11
  const options = program.getCompilerOptions();
@@ -20,6 +22,7 @@ function getPlugins(program) {
20
22
  if (options.tstlVerbose) {
21
23
  console.log(`Loaded ${pluginsFromOptions.length} plugins`);
22
24
  }
25
+ performance.endSection("getPlugins");
23
26
  return { diagnostics, plugins: pluginsFromOptions };
24
27
  }
25
28
  exports.getPlugins = getPlugins;
@@ -21,223 +21,238 @@ class ResolutionContext {
21
21
  this.program = program;
22
22
  this.options = options;
23
23
  this.emitHost = emitHost;
24
- this.resultsCache = new Map();
24
+ this.diagnostics = [];
25
+ this.resolvedFiles = new Map();
26
+ this.processedDependencies = new Set();
27
+ // value is false if already searched but not found
28
+ this.pathToFile = new Map();
25
29
  this.noResolvePaths = new Set(options.noResolvePaths);
26
30
  }
27
- resolve(file, required) {
31
+ addAndResolveDependencies(file) {
32
+ if (this.resolvedFiles.has(file.fileName))
33
+ return;
34
+ this.resolvedFiles.set(file.fileName, file);
35
+ for (const required of findRequiredPaths(file.code)) {
36
+ // Do not resolve noResolution paths
37
+ if (required.startsWith("@NoResolution:")) {
38
+ // Remove @NoResolution prefix if not building in library mode
39
+ if (!isBuildModeLibrary(this.program)) {
40
+ const path = required.replace("@NoResolution:", "");
41
+ replaceRequireInCode(file, required, path);
42
+ replaceRequireInSourceMap(file, required, path);
43
+ }
44
+ // Skip
45
+ continue;
46
+ }
47
+ // Try to resolve the import starting from the directory `file` is in
48
+ this.resolveImport(file, required);
49
+ }
50
+ }
51
+ resolveImport(file, required) {
52
+ // Do no resolve lualib - always use the lualib of the application entry point, not the lualib from external packages
53
+ if (required === "lualib_bundle") {
54
+ this.resolvedFiles.set("lualib_bundle", { fileName: "lualib_bundle", code: "" });
55
+ return;
56
+ }
28
57
  if (this.noResolvePaths.has(required)) {
29
58
  if (this.options.tstlVerbose) {
30
59
  console.log(`Skipping module resolution of ${required} as it is in the tsconfig noResolvePaths.`);
31
60
  }
32
- return { resolvedFiles: [], diagnostics: [] };
61
+ return;
33
62
  }
34
- const resolvedDependency = resolveDependency(file, required, this.program, this.emitHost);
35
- if (resolvedDependency) {
36
- if (this.options.tstlVerbose) {
37
- console.log(`Resolved ${required} to ${(0, utils_1.normalizeSlashes)(resolvedDependency)}`);
38
- }
39
- // Figure out resolved require path and dependency output path
40
- const resolvedRequire = (0, transpiler_1.getEmitPathRelativeToOutDir)(resolvedDependency, this.program);
41
- if (shouldRewriteRequires(resolvedDependency, this.program)) {
42
- replaceRequireInCode(file, required, resolvedRequire);
43
- replaceRequireInSourceMap(file, required, resolvedRequire);
44
- }
45
- // Check cache to prevent resolving nested dependencies double to break dependency loops
46
- if (this.resultsCache.has(resolvedDependency)) {
47
- if (this.options.tstlVerbose) {
48
- console.log(`Resolution cache hit for ${(0, utils_1.normalizeSlashes)(resolvedDependency)}`);
49
- }
50
- return this.resultsCache.get(resolvedDependency);
51
- }
52
- // If dependency is not part of project, add dependency to output and resolve its dependencies recursively
53
- if (shouldIncludeDependency(resolvedDependency, this.program)) {
54
- // If dependency resolved successfully, read its content
55
- const dependencyContent = this.emitHost.readFile(resolvedDependency);
56
- if (dependencyContent === undefined) {
57
- return { resolvedFiles: [], diagnostics: [(0, diagnostics_1.couldNotReadDependency)(resolvedDependency)] };
58
- }
59
- const dependency = {
60
- fileName: resolvedDependency,
61
- code: dependencyContent,
62
- };
63
- const nestedDependencies = resolveFileDependencies(dependency, this);
64
- // Cache result and return
65
- const result = {
66
- resolvedFiles: [dependency, ...nestedDependencies.resolvedFiles],
67
- diagnostics: [...nestedDependencies.diagnostics],
68
- };
69
- this.resultsCache.set(resolvedDependency, result);
70
- return result;
71
- }
72
- else {
73
- const result = {
74
- resolvedFiles: [],
75
- diagnostics: [],
76
- };
77
- this.resultsCache.set(resolvedDependency, result);
78
- return result;
79
- }
63
+ const dependencyPath = this.resolveDependencyPath(file, required);
64
+ if (!dependencyPath)
65
+ return this.couldNotResolveImport(required, file);
66
+ if (this.options.tstlVerbose) {
67
+ console.log(`Resolved ${required} to ${(0, utils_1.normalizeSlashes)(dependencyPath)}`);
80
68
  }
81
- else {
82
- const fallbackRequire = fallbackResolve(required, (0, transpiler_1.getSourceDir)(this.program), path.dirname(file.fileName));
83
- replaceRequireInCode(file, required, fallbackRequire);
84
- replaceRequireInSourceMap(file, required, fallbackRequire);
85
- return {
86
- resolvedFiles: [],
87
- diagnostics: [
88
- (0, diagnostics_1.couldNotResolveRequire)(required, path.relative((0, transpiler_1.getProjectRoot)(this.program), file.fileName)),
89
- ],
90
- };
69
+ this.processDependency(dependencyPath);
70
+ // Figure out resolved require path and dependency output path
71
+ if (shouldRewriteRequires(dependencyPath, this.program)) {
72
+ const resolvedRequire = (0, transpiler_1.getEmitPathRelativeToOutDir)(dependencyPath, this.program);
73
+ replaceRequireInCode(file, required, resolvedRequire);
74
+ replaceRequireInSourceMap(file, required, resolvedRequire);
91
75
  }
92
76
  }
93
- }
94
- function resolveDependencies(program, files, emitHost) {
95
- const outFiles = [...files];
96
- const diagnostics = [];
97
- const options = program.getCompilerOptions();
98
- const resolutionContext = new ResolutionContext(program, options, emitHost);
99
- // Resolve dependencies for all processed files
100
- for (const file of files) {
101
- if (options.tstlVerbose) {
102
- console.log(`Resolving dependencies for ${(0, utils_1.normalizeSlashes)(file.fileName)}`);
77
+ processDependency(dependencyPath) {
78
+ if (this.processedDependencies.has(dependencyPath))
79
+ return;
80
+ this.processedDependencies.add(dependencyPath);
81
+ if (!shouldIncludeDependency(dependencyPath, this.program))
82
+ return;
83
+ // If dependency is not part of project, add dependency to output and resolve its dependencies recursively
84
+ const dependencyContent = this.emitHost.readFile(dependencyPath);
85
+ if (dependencyContent === undefined) {
86
+ this.diagnostics.push((0, diagnostics_1.couldNotReadDependency)(dependencyPath));
87
+ return;
103
88
  }
104
- const resolutionResult = resolveFileDependencies(file, resolutionContext);
105
- outFiles.push(...resolutionResult.resolvedFiles);
106
- diagnostics.push(...resolutionResult.diagnostics);
89
+ const dependency = {
90
+ fileName: dependencyPath,
91
+ code: dependencyContent,
92
+ };
93
+ this.addAndResolveDependencies(dependency);
107
94
  }
108
- return { resolvedFiles: deduplicateResolvedFiles(outFiles), diagnostics };
109
- }
110
- exports.resolveDependencies = resolveDependencies;
111
- function deduplicateResolvedFiles(files) {
112
- return [...new Map(files.map(f => [f.fileName, f])).values()];
113
- }
114
- function resolveFileDependencies(file, context) {
115
- const dependencies = [];
116
- const diagnostics = [];
117
- for (const required of findRequiredPaths(file.code)) {
118
- // Do no resolve lualib - always use the lualib of the application entry point, not the lualib from external packages
119
- if (required === "lualib_bundle") {
120
- dependencies.push({ fileName: "lualib_bundle", code: "" });
121
- continue;
95
+ couldNotResolveImport(required, file) {
96
+ const fallbackRequire = fallbackResolve(required, (0, transpiler_1.getSourceDir)(this.program), path.dirname(file.fileName));
97
+ replaceRequireInCode(file, required, fallbackRequire);
98
+ replaceRequireInSourceMap(file, required, fallbackRequire);
99
+ this.diagnostics.push((0, diagnostics_1.couldNotResolveRequire)(required, path.relative((0, transpiler_1.getProjectRoot)(this.program), file.fileName)));
100
+ }
101
+ resolveDependencyPath(requiringFile, dependency) {
102
+ var _a;
103
+ const fileDirectory = path.dirname(requiringFile.fileName);
104
+ if (this.options.tstlVerbose) {
105
+ console.log(`Resolving "${dependency}" from ${(0, utils_1.normalizeSlashes)(requiringFile.fileName)}`);
122
106
  }
123
- // Do not resolve noResolution paths
124
- if (required.startsWith("@NoResolution:")) {
125
- // Remove @NoResolution prefix if not building in library mode
126
- if (!isBuildModeLibrary(context.program)) {
127
- const path = required.replace("@NoResolution:", "");
128
- replaceRequireInCode(file, required, path);
129
- replaceRequireInSourceMap(file, required, path);
130
- }
131
- // Skip
132
- continue;
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;
133
114
  }
134
- // Try to resolve the import starting from the directory `file` is in
135
- const resolvedDependency = context.resolve(file, required);
136
- dependencies.push(...resolvedDependency.resolvedFiles);
137
- diagnostics.push(...resolvedDependency.diagnostics);
138
- }
139
- return { resolvedFiles: deduplicateResolvedFiles(dependencies), diagnostics };
140
- }
141
- function resolveDependency(requiringFile, dependency, program, emitHost) {
142
- var _a;
143
- const options = program.getCompilerOptions();
144
- const fileDirectory = path.dirname(requiringFile.fileName);
145
- if (options.tstlVerbose) {
146
- console.log(`Resolving "${dependency}" from ${(0, utils_1.normalizeSlashes)(requiringFile.fileName)}`);
147
- }
148
- // Check if the import is relative
149
- const isRelative = ["/", "./", "../"].some(p => dependency.startsWith(p));
150
- // If the import is relative, always resolve it relative to the requiring file
151
- // If the import is not relative, resolve it relative to options.baseUrl if it is set
152
- const relativeTo = isRelative ? fileDirectory : (_a = options.baseUrl) !== null && _a !== void 0 ? _a : fileDirectory;
153
- // Check if file is a file in the project
154
- const resolvedPath = path.join(relativeTo, dependency);
155
- const possibleProjectFiles = [
156
- resolvedPath,
157
- resolvedPath + ".ts",
158
- path.join(resolvedPath, "index.ts"),
159
- resolvedPath + ".tsx",
160
- path.join(resolvedPath, "index.tsx"), // tsx index
161
- ];
162
- for (const possibleFile of possibleProjectFiles) {
163
- if (isProjectFile(possibleFile, program)) {
164
- return possibleFile;
115
+ // Check if the import is relative
116
+ const isRelative = ["/", "./", "../"].some(p => dependency.startsWith(p));
117
+ // If the import is relative, always resolve it relative to the requiring file
118
+ // If the import is not relative, resolve it relative to options.baseUrl if it is set
119
+ const relativeTo = isRelative ? fileDirectory : (_a = this.options.baseUrl) !== null && _a !== void 0 ? _a : fileDirectory;
120
+ // Check if file is a file in the project
121
+ const resolvedPath = path.join(relativeTo, dependencyPath);
122
+ const fileFromPath = this.getFileFromPath(resolvedPath);
123
+ if (fileFromPath)
124
+ return fileFromPath;
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;
165
130
  }
166
- }
167
- // Check if this is a lua file in the project sources
168
- const possibleLuaProjectFiles = [
169
- resolvedPath + ".lua",
170
- path.join(resolvedPath, "index.lua"),
171
- path.join(resolvedPath, "init.lua"), // lua looks for <require>/init.lua if it cannot find <require>.lua
172
- ];
173
- for (const possibleFile of possibleLuaProjectFiles) {
174
- if (emitHost.fileExists(possibleFile)) {
175
- return possibleFile;
131
+ // Not a TS file in our project sources, use resolver to check if we can find dependency
132
+ try {
133
+ const resolveResult = resolver.resolveSync({}, fileDirectory, dependencyPath);
134
+ if (resolveResult)
135
+ return resolveResult;
176
136
  }
177
- }
178
- // Check if this is a sibling of a required lua file
179
- if (requiringFile.fileName.endsWith(".lua")) {
180
- const luaFilePath = resolveLuaPath(fileDirectory, dependency, emitHost);
181
- if (luaFilePath) {
182
- return luaFilePath;
137
+ catch (e) {
138
+ // resolveSync errors if it fails to resolve
183
139
  }
140
+ return undefined;
184
141
  }
185
- // Not a TS file in our project sources, use resolver to check if we can find dependency
186
- try {
187
- const resolveResult = resolver.resolveSync({}, fileDirectory, dependency);
188
- if (resolveResult) {
189
- return resolveResult;
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
+ }
190
158
  }
159
+ return undefined;
191
160
  }
192
- catch (e) {
193
- // resolveSync errors if it fails to resolve
161
+ getFileFromPath(resolvedPath) {
162
+ const existingFile = this.pathToFile.get(resolvedPath);
163
+ if (existingFile)
164
+ return existingFile;
165
+ if (existingFile === false)
166
+ return undefined;
167
+ const file = this.searchForFileFromPath(resolvedPath);
168
+ this.pathToFile.set(resolvedPath, file !== null && file !== void 0 ? file : false);
169
+ return file;
194
170
  }
195
- return undefined;
196
- }
197
- function resolveLuaPath(fromPath, dependency, emitHost) {
198
- const splitDependency = dependency.split(".");
199
- if (splitDependency.length === 1) {
200
- // If dependency has just one part (the file), look for a lua file with that name
201
- const fileDirectory = walkUpFileTreeUntil(fromPath, dir => emitHost.fileExists(path.join(dir, dependency) + ".lua"));
202
- if (fileDirectory) {
203
- return path.join(fileDirectory, dependency) + ".lua";
171
+ searchForFileFromPath(resolvedPath) {
172
+ const possibleProjectFiles = [
173
+ resolvedPath,
174
+ resolvedPath + ".ts",
175
+ path.join(resolvedPath, "index.ts"),
176
+ resolvedPath + ".tsx",
177
+ path.join(resolvedPath, "index.tsx"), // tsx index
178
+ ];
179
+ for (const possibleFile of possibleProjectFiles) {
180
+ if (isProjectFile(possibleFile, this.program)) {
181
+ return possibleFile;
182
+ }
183
+ }
184
+ // Check if this is a lua file in the project sources
185
+ const possibleLuaProjectFiles = [
186
+ resolvedPath + ".lua",
187
+ path.join(resolvedPath, "index.lua"),
188
+ path.join(resolvedPath, "init.lua"), // lua looks for <require>/init.lua if it cannot find <require>.lua
189
+ ];
190
+ for (const possibleFile of possibleLuaProjectFiles) {
191
+ if (this.emitHost.fileExists(possibleFile)) {
192
+ return possibleFile;
193
+ }
204
194
  }
205
195
  }
206
- else {
207
- // If dependency has multiple parts, look for the first directory of the require path, which must be in the lua root
208
- const luaRoot = walkUpFileTreeUntil(fromPath, dir => emitHost.directoryExists(path.join(dir, splitDependency[0])));
209
- if (luaRoot) {
210
- return path.join(luaRoot, dependency.replace(/\./g, path.sep)) + ".lua";
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
+ }
211
228
  }
212
229
  }
213
230
  }
214
- function walkUpFileTreeUntil(fromDirectory, predicate) {
215
- const currentDir = path.normalize(fromDirectory).split(path.sep);
216
- while (currentDir.length > 0) {
217
- const dir = currentDir.join(path.sep);
218
- if (predicate(dir)) {
219
- return dir;
231
+ function resolveDependencies(program, files, emitHost) {
232
+ const options = program.getCompilerOptions();
233
+ const resolutionContext = new ResolutionContext(program, options, emitHost);
234
+ // Resolve dependencies for all processed files
235
+ for (const file of files) {
236
+ if (options.tstlVerbose) {
237
+ console.log(`Resolving dependencies for ${(0, utils_1.normalizeSlashes)(file.fileName)}`);
220
238
  }
221
- currentDir.pop();
239
+ resolutionContext.addAndResolveDependencies(file);
222
240
  }
223
- return undefined;
241
+ return { resolvedFiles: [...resolutionContext.resolvedFiles.values()], diagnostics: resolutionContext.diagnostics };
224
242
  }
243
+ exports.resolveDependencies = resolveDependencies;
225
244
  function shouldRewriteRequires(resolvedDependency, program) {
226
- return !isNodeModulesFile(resolvedDependency) || !isBuildModeLibrary(program);
245
+ return !isBuildModeLibrary(program) || !isNodeModulesFile(resolvedDependency);
227
246
  }
228
247
  function shouldIncludeDependency(resolvedDependency, program) {
229
248
  // Never include lua files (again) that are transpiled from project sources
230
- if (!hasSourceFileInProject(resolvedDependency, program)) {
231
- // Always include lua files not in node_modules (internal lua sources)
232
- if (!isNodeModulesFile(resolvedDependency)) {
233
- return true;
234
- }
235
- else {
236
- // Only include node_modules files if not in library mode
237
- return !isBuildModeLibrary(program);
238
- }
239
- }
240
- return false;
249
+ if (hasSourceFileInProject(resolvedDependency, program))
250
+ return false;
251
+ // Always include lua files not in node_modules (internal lua sources)
252
+ if (!isNodeModulesFile(resolvedDependency))
253
+ return true;
254
+ // Only include node_modules files if not in library mode
255
+ return !isBuildModeLibrary(program);
241
256
  }
242
257
  function isBuildModeLibrary(program) {
243
258
  return program.getCompilerOptions().buildMode === CompilerOptions_1.BuildMode.Library;
@@ -245,7 +260,7 @@ function isBuildModeLibrary(program) {
245
260
  function findRequiredPaths(code) {
246
261
  // Find all require("<path>") paths in a lua code string
247
262
  const paths = [];
248
- const pattern = /(^|\s|;|=)require\("(.+?)"\)/g;
263
+ const pattern = /(^|\s|;|=|\()require\("(.+?)"\)/g;
249
264
  // eslint-disable-next-line @typescript-eslint/ban-types
250
265
  let match;
251
266
  while ((match = pattern.exec(code))) {
@@ -257,7 +272,7 @@ function replaceRequireInCode(file, originalRequire, newRequire) {
257
272
  const requirePath = (0, utils_1.formatPathToLuaPath)(newRequire.replace(".lua", ""));
258
273
  // Escape special characters to prevent the regex from breaking...
259
274
  const escapedRequire = originalRequire.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
260
- 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}")`);
261
276
  }
262
277
  function replaceRequireInSourceMap(file, originalRequire, newRequire) {
263
278
  const requirePath = (0, utils_1.formatPathToLuaPath)(newRequire.replace(".lua", ""));
@@ -302,4 +317,13 @@ function fallbackResolve(required, sourceRootDir, fileDir) {
302
317
  .filter(s => s !== "." && s !== "..")
303
318
  .join(path.sep));
304
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
+ }
305
329
  //# sourceMappingURL=resolve.js.map