X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 39 additions & 43 deletions src/transpilation/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ class ResolutionContext {
console.log(`Resolving "${dependency}" from ${normalizeSlashes(requiringFile.fileName)}`);
}

const requiredFromLuaFile = requiringFile.fileName.endsWith(".lua");
const dependencyPath = requiredFromLuaFile ? luaRequireToPath(dependency) : dependency;

if (requiredFromLuaFile && isNodeModulesFile(requiringFile.fileName)) {
// If requiring file is in lua module, try to resolve sibling in that file first
const resolvedNodeModulesFile = this.resolveLuaDependencyPathFromNodeModules(requiringFile, dependencyPath);
if (resolvedNodeModulesFile) return resolvedNodeModulesFile;
}

// Check if the import is relative
const isRelative = ["/", "./", "../"].some(p => dependency.startsWith(p));

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

// Check if file is a file in the project
const resolvedPath = path.join(relativeTo, dependency);
const resolvedPath = path.join(relativeTo, dependencyPath);
const fileFromPath = this.getFileFromPath(resolvedPath);
if (fileFromPath) return fileFromPath;

// Check if this is a sibling of a required lua file
if (requiringFile.fileName.endsWith(".lua")) {
const luaFilePath = resolveLuaPath(fileDirectory, dependency, this.emitHost);
if (luaFilePath) {
return luaFilePath;
}
}

// Not a TS file in our project sources, use resolver to check if we can find dependency
try {
const resolveResult = resolver.resolveSync({}, fileDirectory, dependency);
const resolveResult = resolver.resolveSync({}, fileDirectory, dependencyPath);
if (resolveResult) return resolveResult;
} catch (e) {
// resolveSync errors if it fails to resolve
Expand All @@ -157,6 +158,30 @@ class ResolutionContext {
return undefined;
}

private resolveLuaDependencyPathFromNodeModules(
requiringFile: ProcessedFile,
dependency: string
): string | undefined {
// We don't know for sure where the lua root is, so guess it is at package root
const splitPath = path.normalize(requiringFile.fileName).split(path.sep);
let packageRootIndex = splitPath.lastIndexOf("node_modules") + 2;
let packageRoot = splitPath.slice(0, packageRootIndex).join(path.sep);

while (packageRootIndex < splitPath.length) {
// Try to find lua file relative to currently guessed Lua root
const resolvedPath = path.join(packageRoot, dependency);
const fileFromPath = this.getFileFromPath(resolvedPath);
if (fileFromPath) {
return fileFromPath;
} else {
// Did not find file at current root, try again one directory deeper
packageRoot = path.join(packageRoot, splitPath[packageRootIndex++]);
}
}

return undefined;
}

// value is false if already searched but not found
private pathToFile = new Map<string, string | false>();

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

function resolveLuaPath(fromPath: string, dependency: string, emitHost: EmitHost) {
const splitDependency = dependency.split(".");
if (splitDependency.length === 1) {
// If dependency has just one part (the file), look for a lua file with that name
const fileDirectory = walkUpFileTreeUntil(fromPath, dir =>
emitHost.fileExists(path.join(dir, dependency) + ".lua")
);
if (fileDirectory) {
return path.join(fileDirectory, dependency) + ".lua";
}
} else {
// If dependency has multiple parts, look for the first directory of the require path, which must be in the lua root
const luaRoot = walkUpFileTreeUntil(fromPath, dir =>
emitHost.directoryExists(path.join(dir, splitDependency[0]))
);
if (luaRoot) {
return path.join(luaRoot, dependency.replace(/\./g, path.sep)) + ".lua";
}
}
}

function walkUpFileTreeUntil(fromDirectory: string, predicate: (dir: string) => boolean) {
const currentDir = path.normalize(fromDirectory).split(path.sep);
while (currentDir.length > 0) {
const dir = currentDir.join(path.sep);
if (predicate(dir)) {
return dir;
}
currentDir.pop();
}
return undefined;
}

function shouldRewriteRequires(resolvedDependency: string, program: ts.Program) {
return !isBuildModeLibrary(program) || !isNodeModulesFile(resolvedDependency);
}
Expand Down Expand Up @@ -344,3 +336,7 @@ function fallbackResolve(required: string, sourceRootDir: string, fileDir: strin
.join(path.sep)
);
}

function luaRequireToPath(requirePath: string): string {
return requirePath.replace(/\./g, path.sep);
}
13 changes: 13 additions & 0 deletions test/transpile/module-resolution.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,3 +525,16 @@ test("require matches correct pattern", () => {
.addExtraFile("c.lua", "return function(self, a) return a end")
.expectToEqual({ addResult: 3 + 5, callResult: "foo" });
});

// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1307
test("lualib_module with parent directory import (#1307)", () => {
const projectDir = path.join(__dirname, "module-resolution", "project-with-dependency-with-same-file-names");
const inputProject = path.join(projectDir, "tsconfig.json");

util.testProject(inputProject).setMainFileName(path.join(projectDir, "index.ts")).expectToEqual({
// eslint-disable-next-line @typescript-eslint/naming-convention
BASE_CONSTANT: 123,
// eslint-disable-next-line @typescript-eslint/naming-convention
FEATURE_CONSTANT: 456,
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "mymodule";

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
X Tutup