Skip to content

Commit 1e64c54

Browse files
- some fixes to the module resolution
- added test case
1 parent 4164d68 commit 1e64c54

9 files changed

Lines changed: 103 additions & 14 deletions

File tree

src/transpilation/plugins.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export interface Plugin {
5151
requiringFile: string,
5252
options: CompilerOptions,
5353
emitHost: EmitHost
54-
) => string | undefined
54+
) => string | undefined;
5555
}
5656

5757
export function getPlugins(program: ts.Program): { diagnostics: ts.Diagnostic[]; plugins: Plugin[] } {

src/transpilation/resolve.ts

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ class ResolutionContext {
7979
return;
8080
}
8181

82-
const dependencyPath = this.resolveDependencyPathsWithPlugins(file, required.requirePath) ?? this.resolveDependencyPath(file, required.requirePath);
82+
const dependencyPath =
83+
this.resolveDependencyPathsWithPlugins(file, required.requirePath) ??
84+
this.resolveDependencyPath(file, required.requirePath);
85+
8386
if (!dependencyPath) return this.couldNotResolveImport(required, file);
8487

8588
if (this.options.tstlVerbose) {
@@ -99,28 +102,50 @@ class ResolutionContext {
99102
const requiredFromLuaFile = required.fileName.endsWith(".lua");
100103
const dependencyPath = requiredFromLuaFile ? luaRequireToPath(dependency) : dependency;
101104

102-
for (const p of this.plugins) {
103-
if (p.moduleResolution != null) {
104-
const pluginResolvedPath = p.moduleResolution(dependency, dependencyPath, this.options, this.emitHost)
105+
for (const plugin of this.plugins) {
106+
if (plugin.moduleResolution != null) {
107+
const pluginResolvedPath = plugin.moduleResolution(
108+
dependency,
109+
dependencyPath,
110+
this.options,
111+
this.emitHost
112+
);
105113
if (pluginResolvedPath !== undefined) {
106-
107114
// If lua file is in node_module
108115
if (requiredFromLuaFile && isNodeModulesFile(required.fileName)) {
109116
// If requiring file is in lua module, try to resolve sibling in that file first
110-
const resolvedNodeModulesFile = this.resolveLuaDependencyPathFromNodeModules(required, pluginResolvedPath);
117+
const resolvedNodeModulesFile = this.resolveLuaDependencyPathFromNodeModules(
118+
required,
119+
pluginResolvedPath
120+
);
111121
if (resolvedNodeModulesFile) {
112122
if (this.options.tstlVerbose) {
113-
console.log(`Resolved file path for module ${dependency} to path ${dependencyPath} using plugin.`)
123+
console.log(
124+
`Resolved file path for module ${dependency} to path ${dependencyPath} using plugin.`
125+
);
114126
}
115-
return resolvedNodeModulesFile
127+
return resolvedNodeModulesFile;
116128
}
117129
}
118130

119-
if (this.getFileFromPath(pluginResolvedPath)) {
131+
const isRelative = ["/", "./", "../"].some(p => pluginResolvedPath.startsWith(p));
132+
133+
// // If the import is relative, always resolve it relative to the requiring file
134+
// // If the import is not relative, resolve it relative to options.baseUrl if it is set
135+
const fileDirectory = path.dirname(required.fileName);
136+
const relativeTo = isRelative ? fileDirectory : this.options.baseUrl ?? fileDirectory;
137+
138+
// // Check if file is a file in the project
139+
const resolvedPath = path.join(relativeTo, pluginResolvedPath);
140+
const fileFromPath = this.getFileFromPath(resolvedPath);
141+
142+
if (fileFromPath) {
120143
if (this.options.tstlVerbose) {
121-
console.log(`Resolved file path for module ${dependency} to path ${dependencyPath} using plugin.`)
144+
console.log(
145+
`Resolved file path for module ${dependency} to path ${resolvedPath} using plugin.`
146+
);
122147
}
123-
return pluginResolvedPath;
148+
return fileFromPath;
124149
}
125150
}
126151
}
@@ -312,7 +337,12 @@ class ResolutionContext {
312337
}
313338
}
314339

315-
export function resolveDependencies(program: ts.Program, files: ProcessedFile[], emitHost: EmitHost, plugins: Plugin[]): ResolutionResult {
340+
export function resolveDependencies(
341+
program: ts.Program,
342+
files: ProcessedFile[],
343+
emitHost: EmitHost,
344+
plugins: Plugin[]
345+
): ResolutionResult {
316346
const options = program.getCompilerOptions() as CompilerOptions;
317347

318348
const resolutionContext = new ResolutionContext(program, options, emitHost, plugins);

src/transpilation/transpiler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class Transpiler {
103103
program: ts.Program,
104104
diagnostics: ts.Diagnostic[],
105105
files: ProcessedFile[],
106-
plugins : Plugin[]
106+
plugins: Plugin[]
107107
): { emitPlan: EmitFile[] } {
108108
performance.startSection("getEmitPlan");
109109
const options = program.getCompilerOptions() as CompilerOptions;

test/transpile/module-resolution.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,36 @@ test("paths without baseUrl is error", () => {
647647
util.testFunction``.setOptions({ paths: {} }).expectToHaveDiagnostics([pathsWithoutBaseUrl.code]);
648648
});
649649

650+
test("module resolution using plugin", () => {
651+
const baseProjectPath = path.resolve(__dirname, "module-resolution", "project-with-module-resolution-plugin");
652+
const projectTsConfig = path.join(baseProjectPath, "tsconfig.json");
653+
const mainFile = path.join(baseProjectPath, "src", "main.ts");
654+
655+
const luaResult = util
656+
.testProject(projectTsConfig)
657+
.setMainFileName(mainFile)
658+
.setOptions({
659+
luaPlugins: [
660+
{
661+
name: path.join(__dirname, "./plugins/moduleResolution.ts"),
662+
},
663+
],
664+
})
665+
.expectToHaveNoDiagnostics()
666+
.getLuaResult();
667+
668+
expect(luaResult.transpiledFiles).toHaveLength(2);
669+
let hasResolvedFile = false;
670+
for (const f of luaResult.transpiledFiles) {
671+
hasResolvedFile = f.outPath.endsWith("bar.lua");
672+
if (hasResolvedFile) {
673+
break;
674+
}
675+
}
676+
677+
expect(hasResolvedFile).toBe(true);
678+
});
679+
650680
function snapshotPaths(files: tstl.TranspiledFile[]) {
651681
return files.map(f => normalizeSlashes(f.outPath).split("module-resolution")[1]).sort();
652682
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
local ____exports = {}
2+
function ____exports.foo(self)
3+
return "foo"
4+
end
5+
return ____exports
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export declare function foo(): string;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import { foo } from "./lua_sources/foo";
2+
export const result = foo();
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"compilerOptions": {
3+
"strict": true,
4+
"target": "esnext",
5+
"lib": ["esnext"],
6+
"types": [],
7+
"outDir": "./dist"
8+
}
9+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type * as tstl from "../../../src";
2+
3+
const plugin: tstl.Plugin = {
4+
moduleResolution(moduleIdentifier, requiringFile) {
5+
if (moduleIdentifier.includes("foo")) {
6+
return requiringFile.replace("foo", "bar");
7+
}
8+
},
9+
};
10+
11+
// eslint-disable-next-line import/no-default-export
12+
export default plugin;

0 commit comments

Comments
 (0)