@@ -9,6 +9,7 @@ import { formatPathToLuaPath, normalizeSlashes, trimExtension } from "../utils";
99import { couldNotReadDependency , couldNotResolveRequire } from "./diagnostics" ;
1010import { BuildMode , CompilerOptions } from "../CompilerOptions" ;
1111import { findLuaRequires , LuaRequire } from "./find-lua-requires" ;
12+ import { Plugin } from "./plugins" ;
1213
1314const resolver = resolve . ResolverFactory . createResolver ( {
1415 extensions : [ ".lua" ] ,
@@ -33,7 +34,8 @@ class ResolutionContext {
3334 constructor (
3435 public readonly program : ts . Program ,
3536 public readonly options : CompilerOptions ,
36- private readonly emitHost : EmitHost
37+ private readonly emitHost : EmitHost ,
38+ private readonly plugins : Plugin [ ]
3739 ) {
3840 this . noResolvePaths = new Set ( options . noResolvePaths ) ;
3941 }
@@ -77,7 +79,10 @@ class ResolutionContext {
7779 return ;
7880 }
7981
80- const dependencyPath = this . resolveDependencyPath ( file , required . requirePath ) ;
82+ const dependencyPath =
83+ this . resolveDependencyPathsWithPlugins ( file , required . requirePath ) ??
84+ this . resolveDependencyPath ( file , required . requirePath ) ;
85+
8186 if ( ! dependencyPath ) return this . couldNotResolveImport ( required , file ) ;
8287
8388 if ( this . options . tstlVerbose ) {
@@ -93,8 +98,65 @@ class ResolutionContext {
9398 }
9499 }
95100
101+ private resolveDependencyPathsWithPlugins ( requiringFile : ProcessedFile , dependency : string ) {
102+ const requiredFromLuaFile = requiringFile . fileName . endsWith ( ".lua" ) ;
103+ for ( const plugin of this . plugins ) {
104+ if ( plugin . moduleResolution != null ) {
105+ const pluginResolvedPath = plugin . moduleResolution (
106+ dependency ,
107+ requiringFile . fileName ,
108+ this . options ,
109+ this . emitHost
110+ ) ;
111+ if ( pluginResolvedPath !== undefined ) {
112+ // If lua file is in node_module
113+ if ( requiredFromLuaFile && isNodeModulesFile ( requiringFile . fileName ) ) {
114+ // If requiring file is in lua module, try to resolve sibling in that file first
115+ const resolvedNodeModulesFile = this . resolveLuaDependencyPathFromNodeModules (
116+ requiringFile ,
117+ pluginResolvedPath
118+ ) ;
119+ if ( resolvedNodeModulesFile ) {
120+ if ( this . options . tstlVerbose ) {
121+ console . log (
122+ `Resolved file path for module ${ dependency } to path ${ pluginResolvedPath } using plugin.`
123+ ) ;
124+ }
125+ return resolvedNodeModulesFile ;
126+ }
127+ }
128+
129+ const resolvedPath = this . formatPathToFile ( pluginResolvedPath , requiringFile ) ;
130+ const fileFromPath = this . getFileFromPath ( resolvedPath ) ;
131+
132+ if ( fileFromPath ) {
133+ if ( this . options . tstlVerbose ) {
134+ console . log (
135+ `Resolved file path for module ${ dependency } to path ${ pluginResolvedPath } using plugin.`
136+ ) ;
137+ }
138+ return fileFromPath ;
139+ }
140+ }
141+ }
142+ }
143+ }
144+
96145 public processedDependencies = new Set < string > ( ) ;
97146
147+ private formatPathToFile ( targetPath : string , required : ProcessedFile ) {
148+ const isRelative = [ "/" , "./" , "../" ] . some ( p => targetPath . startsWith ( p ) ) ;
149+
150+ // // If the import is relative, always resolve it relative to the requiring file
151+ // // If the import is not relative, resolve it relative to options.baseUrl if it is set
152+ const fileDirectory = path . dirname ( required . fileName ) ;
153+ const relativeTo = isRelative ? fileDirectory : this . options . baseUrl ?? fileDirectory ;
154+
155+ // // Check if file is a file in the project
156+ const resolvedPath = path . join ( relativeTo , targetPath ) ;
157+ return resolvedPath ;
158+ }
159+
98160 private processDependency ( dependencyPath : string ) : void {
99161 if ( this . processedDependencies . has ( dependencyPath ) ) return ;
100162 this . processedDependencies . add ( dependencyPath ) ;
@@ -140,15 +202,8 @@ class ResolutionContext {
140202 if ( resolvedNodeModulesFile ) return resolvedNodeModulesFile ;
141203 }
142204
143- // Check if the import is relative
144- const isRelative = [ "/" , "./" , "../" ] . some ( p => dependency . startsWith ( p ) ) ;
145-
146- // If the import is relative, always resolve it relative to the requiring file
147- // If the import is not relative, resolve it relative to options.baseUrl if it is set
148- const relativeTo = isRelative ? fileDirectory : this . options . baseUrl ?? fileDirectory ;
149-
150205 // Check if file is a file in the project
151- const resolvedPath = path . join ( relativeTo , dependencyPath ) ;
206+ const resolvedPath = this . formatPathToFile ( dependencyPath , requiringFile ) ;
152207 const fileFromPath = this . getFileFromPath ( resolvedPath ) ;
153208 if ( fileFromPath ) return fileFromPath ;
154209
@@ -235,6 +290,7 @@ class ResolutionContext {
235290 path . join ( resolvedPath , "index.lua" ) , // lua index file in sources
236291 path . join ( resolvedPath , "init.lua" ) , // lua looks for <require>/init.lua if it cannot find <require>.lua
237292 ] ;
293+
238294 for ( const possibleFile of possibleLuaProjectFiles ) {
239295 if ( this . emitHost . fileExists ( possibleFile ) ) {
240296 return possibleFile ;
@@ -277,10 +333,15 @@ class ResolutionContext {
277333 }
278334}
279335
280- export function resolveDependencies ( program : ts . Program , files : ProcessedFile [ ] , emitHost : EmitHost ) : ResolutionResult {
336+ export function resolveDependencies (
337+ program : ts . Program ,
338+ files : ProcessedFile [ ] ,
339+ emitHost : EmitHost ,
340+ plugins : Plugin [ ]
341+ ) : ResolutionResult {
281342 const options = program . getCompilerOptions ( ) as CompilerOptions ;
282343
283- const resolutionContext = new ResolutionContext ( program , options , emitHost ) ;
344+ const resolutionContext = new ResolutionContext ( program , options , emitHost , plugins ) ;
284345
285346 // Resolve dependencies for all processed files
286347 for ( const file of files ) {
0 commit comments