diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index e011f4350..af7981ffc 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -208,13 +208,9 @@ class ResolutionContext { if (resolvedNodeModulesFile) return resolvedNodeModulesFile; } - // Check if file is a file in the project - const resolvedPath = this.formatPathToFile(dependencyPath, requiringFile); - const fileFromPath = this.getFileFromPath(resolvedPath); - if (fileFromPath) return fileFromPath; - - if (this.options.paths) { - // If no file found yet and paths are present, try to find project file via paths mappings + // Bare specifiers: check paths mappings first, matching TypeScript's resolution order. + // TS never applies paths to relative imports, so skip for those. + if (!ts.isExternalModuleNameRelative(dependencyPath) && this.options.paths) { // When baseUrl is not set, resolve paths relative to the tsconfig directory (TS 6.0+ behavior) const pathsBase = this.options.baseUrl ?? @@ -225,6 +221,11 @@ class ResolutionContext { } } + // Check if file is a file in the project + const resolvedPath = this.formatPathToFile(dependencyPath, requiringFile); + const fileFromPath = this.getFileFromPath(resolvedPath); + if (fileFromPath) return fileFromPath; + // Not a TS file in our project sources, use resolver to check if we can find dependency try { const resolveResult = resolver.resolveSync({}, fileDirectory, dependencyPath); diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index e6543df69..35e79ed3d 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -707,6 +707,18 @@ test("supports complicated paths configuration", () => { .expectToEqual({ foo: 314, bar: 271 }); }); +test("paths mapping wins over sibling project file (TS resolution order)", () => { + const baseProjectPath = path.resolve(__dirname, "module-resolution", "paths-vs-project-file"); + const projectPath = path.join(baseProjectPath, "program"); + const projectTsConfig = path.join(projectPath, "tsconfig.json"); + const mainFile = path.join(projectPath, "main.ts"); + + util.testProject(projectTsConfig) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual({ value: "paths-mapped" }); +}); + test("module resolution using plugin", () => { const baseProjectPath = path.resolve(__dirname, "module-resolution", "project-with-module-resolution-plugin"); const projectTsConfig = path.join(baseProjectPath, "tsconfig.json"); diff --git a/test/transpile/module-resolution/paths-vs-project-file/other/alias.ts b/test/transpile/module-resolution/paths-vs-project-file/other/alias.ts new file mode 100644 index 000000000..b09331910 --- /dev/null +++ b/test/transpile/module-resolution/paths-vs-project-file/other/alias.ts @@ -0,0 +1 @@ +export const value = "paths-mapped"; diff --git a/test/transpile/module-resolution/paths-vs-project-file/program/alias.ts b/test/transpile/module-resolution/paths-vs-project-file/program/alias.ts new file mode 100644 index 000000000..565154b68 --- /dev/null +++ b/test/transpile/module-resolution/paths-vs-project-file/program/alias.ts @@ -0,0 +1 @@ +export const value = "project-file"; diff --git a/test/transpile/module-resolution/paths-vs-project-file/program/main.ts b/test/transpile/module-resolution/paths-vs-project-file/program/main.ts new file mode 100644 index 000000000..7b4bc6587 --- /dev/null +++ b/test/transpile/module-resolution/paths-vs-project-file/program/main.ts @@ -0,0 +1,3 @@ +import { value } from "alias"; + +export { value }; diff --git a/test/transpile/module-resolution/paths-vs-project-file/program/tsconfig.json b/test/transpile/module-resolution/paths-vs-project-file/program/tsconfig.json new file mode 100644 index 000000000..26c1a151f --- /dev/null +++ b/test/transpile/module-resolution/paths-vs-project-file/program/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "rootDir": "..", + "outDir": "dist", + "paths": { + "alias": ["../other/alias"] + } + } +}