X Tutup
Skip to content

Commit bd3bced

Browse files
authored
Fix lua file resolutoin using the wrong root dirs (#1316)
1 parent 1001207 commit bd3bced

File tree

9 files changed

+80
-43
lines changed

9 files changed

+80
-43
lines changed

src/transpilation/resolve.ts

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ class ResolutionContext {
126126
console.log(`Resolving "${dependency}" from ${normalizeSlashes(requiringFile.fileName)}`);
127127
}
128128

129+
const requiredFromLuaFile = requiringFile.fileName.endsWith(".lua");
130+
const dependencyPath = requiredFromLuaFile ? luaRequireToPath(dependency) : dependency;
131+
132+
if (requiredFromLuaFile && isNodeModulesFile(requiringFile.fileName)) {
133+
// If requiring file is in lua module, try to resolve sibling in that file first
134+
const resolvedNodeModulesFile = this.resolveLuaDependencyPathFromNodeModules(requiringFile, dependencyPath);
135+
if (resolvedNodeModulesFile) return resolvedNodeModulesFile;
136+
}
137+
129138
// Check if the import is relative
130139
const isRelative = ["/", "./", "../"].some(p => dependency.startsWith(p));
131140

@@ -134,21 +143,13 @@ class ResolutionContext {
134143
const relativeTo = isRelative ? fileDirectory : this.options.baseUrl ?? fileDirectory;
135144

136145
// Check if file is a file in the project
137-
const resolvedPath = path.join(relativeTo, dependency);
146+
const resolvedPath = path.join(relativeTo, dependencyPath);
138147
const fileFromPath = this.getFileFromPath(resolvedPath);
139148
if (fileFromPath) return fileFromPath;
140149

141-
// Check if this is a sibling of a required lua file
142-
if (requiringFile.fileName.endsWith(".lua")) {
143-
const luaFilePath = resolveLuaPath(fileDirectory, dependency, this.emitHost);
144-
if (luaFilePath) {
145-
return luaFilePath;
146-
}
147-
}
148-
149150
// Not a TS file in our project sources, use resolver to check if we can find dependency
150151
try {
151-
const resolveResult = resolver.resolveSync({}, fileDirectory, dependency);
152+
const resolveResult = resolver.resolveSync({}, fileDirectory, dependencyPath);
152153
if (resolveResult) return resolveResult;
153154
} catch (e) {
154155
// resolveSync errors if it fails to resolve
@@ -157,6 +158,30 @@ class ResolutionContext {
157158
return undefined;
158159
}
159160

161+
private resolveLuaDependencyPathFromNodeModules(
162+
requiringFile: ProcessedFile,
163+
dependency: string
164+
): string | undefined {
165+
// We don't know for sure where the lua root is, so guess it is at package root
166+
const splitPath = path.normalize(requiringFile.fileName).split(path.sep);
167+
let packageRootIndex = splitPath.lastIndexOf("node_modules") + 2;
168+
let packageRoot = splitPath.slice(0, packageRootIndex).join(path.sep);
169+
170+
while (packageRootIndex < splitPath.length) {
171+
// Try to find lua file relative to currently guessed Lua root
172+
const resolvedPath = path.join(packageRoot, dependency);
173+
const fileFromPath = this.getFileFromPath(resolvedPath);
174+
if (fileFromPath) {
175+
return fileFromPath;
176+
} else {
177+
// Did not find file at current root, try again one directory deeper
178+
packageRoot = path.join(packageRoot, splitPath[packageRootIndex++]);
179+
}
180+
}
181+
182+
return undefined;
183+
}
184+
160185
// value is false if already searched but not found
161186
private pathToFile = new Map<string, string | false>();
162187

@@ -215,39 +240,6 @@ export function resolveDependencies(program: ts.Program, files: ProcessedFile[],
215240
return { resolvedFiles: [...resolutionContext.resolvedFiles.values()], diagnostics: resolutionContext.diagnostics };
216241
}
217242

218-
function resolveLuaPath(fromPath: string, dependency: string, emitHost: EmitHost) {
219-
const splitDependency = dependency.split(".");
220-
if (splitDependency.length === 1) {
221-
// If dependency has just one part (the file), look for a lua file with that name
222-
const fileDirectory = walkUpFileTreeUntil(fromPath, dir =>
223-
emitHost.fileExists(path.join(dir, dependency) + ".lua")
224-
);
225-
if (fileDirectory) {
226-
return path.join(fileDirectory, dependency) + ".lua";
227-
}
228-
} else {
229-
// If dependency has multiple parts, look for the first directory of the require path, which must be in the lua root
230-
const luaRoot = walkUpFileTreeUntil(fromPath, dir =>
231-
emitHost.directoryExists(path.join(dir, splitDependency[0]))
232-
);
233-
if (luaRoot) {
234-
return path.join(luaRoot, dependency.replace(/\./g, path.sep)) + ".lua";
235-
}
236-
}
237-
}
238-
239-
function walkUpFileTreeUntil(fromDirectory: string, predicate: (dir: string) => boolean) {
240-
const currentDir = path.normalize(fromDirectory).split(path.sep);
241-
while (currentDir.length > 0) {
242-
const dir = currentDir.join(path.sep);
243-
if (predicate(dir)) {
244-
return dir;
245-
}
246-
currentDir.pop();
247-
}
248-
return undefined;
249-
}
250-
251243
function shouldRewriteRequires(resolvedDependency: string, program: ts.Program) {
252244
return !isBuildModeLibrary(program) || !isNodeModulesFile(resolvedDependency);
253245
}
@@ -344,3 +336,7 @@ function fallbackResolve(required: string, sourceRootDir: string, fileDir: strin
344336
.join(path.sep)
345337
);
346338
}
339+
340+
function luaRequireToPath(requirePath: string): string {
341+
return requirePath.replace(/\./g, path.sep);
342+
}

test/transpile/module-resolution.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,3 +525,16 @@ test("require matches correct pattern", () => {
525525
.addExtraFile("c.lua", "return function(self, a) return a end")
526526
.expectToEqual({ addResult: 3 + 5, callResult: "foo" });
527527
});
528+
529+
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1307
530+
test("lualib_module with parent directory import (#1307)", () => {
531+
const projectDir = path.join(__dirname, "module-resolution", "project-with-dependency-with-same-file-names");
532+
const inputProject = path.join(projectDir, "tsconfig.json");
533+
534+
util.testProject(inputProject).setMainFileName(path.join(projectDir, "index.ts")).expectToEqual({
535+
// eslint-disable-next-line @typescript-eslint/naming-convention
536+
BASE_CONSTANT: 123,
537+
// eslint-disable-next-line @typescript-eslint/naming-convention
538+
FEATURE_CONSTANT: 456,
539+
});
540+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "mymodule";

test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/constants.lua

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/constants.lua

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/feature.lua

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.d.ts

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.lua

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

0 commit comments

Comments
 (0)
X Tutup