From 0917a21cd61d9cc54ca4856cf6005400298ac9cd Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sat, 23 Jul 2022 18:23:33 +0200 Subject: [PATCH] Fix lua file resolutoin using the wrong root dirs --- src/transpilation/resolve.ts | 82 +++++++++---------- test/transpile/module-resolution.spec.ts | 13 +++ .../index.ts | 1 + .../node_modules/mymodule/constants.lua | 3 + .../mymodule/feature/constants.lua | 3 + .../node_modules/mymodule/feature/feature.lua | 8 ++ .../node_modules/mymodule/index.d.ts | 2 + .../node_modules/mymodule/index.lua | 10 +++ .../tsconfig.json | 1 + 9 files changed, 80 insertions(+), 43 deletions(-) create mode 100644 test/transpile/module-resolution/project-with-dependency-with-same-file-names/index.ts create mode 100644 test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/constants.lua create mode 100644 test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/constants.lua create mode 100644 test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/feature.lua create mode 100644 test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.d.ts create mode 100644 test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.lua create mode 100644 test/transpile/module-resolution/project-with-dependency-with-same-file-names/tsconfig.json diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index d3f108955..ccf0bdfa4 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -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)); @@ -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 @@ -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(); @@ -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); } @@ -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); +} diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index cd7869c70..c18990225 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -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, + }); +}); diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/index.ts b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/index.ts new file mode 100644 index 000000000..0ab21dc4e --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/index.ts @@ -0,0 +1 @@ +export * from "mymodule"; diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/constants.lua b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/constants.lua new file mode 100644 index 000000000..de5917dd7 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/constants.lua @@ -0,0 +1,3 @@ +local ____exports = {} +____exports.BASE_CONSTANT = 123 +return ____exports diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/constants.lua b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/constants.lua new file mode 100644 index 000000000..9d3bd6110 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/constants.lua @@ -0,0 +1,3 @@ +local ____exports = {} +____exports.FEATURE_CONSTANT = 456 +return ____exports diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/feature.lua b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/feature.lua new file mode 100644 index 000000000..103496eaa --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/feature.lua @@ -0,0 +1,8 @@ +local ____exports = {} +local ____constants = require("constants") +local BASE_CONSTANT = ____constants.BASE_CONSTANT +local ____constants = require("feature.constants") +local FEATURE_CONSTANT = ____constants.FEATURE_CONSTANT +____exports.BASE_CONSTANT = BASE_CONSTANT +____exports.FEATURE_CONSTANT = FEATURE_CONSTANT +return ____exports diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.d.ts b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.d.ts new file mode 100644 index 000000000..90401efad --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.d.ts @@ -0,0 +1,2 @@ +export declare const BASE_CONSTANT: number; +export declare const FEATURE_CONSTANT: number; diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.lua b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.lua new file mode 100644 index 000000000..4e57411d3 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.lua @@ -0,0 +1,10 @@ +local ____exports = {} +do + local ____export = require("feature.feature") + for ____exportKey, ____exportValue in pairs(____export) do + if ____exportKey ~= "default" then + ____exports[____exportKey] = ____exportValue + end + end +end +return ____exports diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/tsconfig.json b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/tsconfig.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/tsconfig.json @@ -0,0 +1 @@ +{}