@@ -126,6 +126,15 @@ class ResolutionContext {
126126 console . log ( `Resolving "${ dependency } " from ${ normalizeSlashes ( requiringFile . fileName ) } ` ) ;
127127 }
128128
129+ const requiredFromLuaFile = requiringFile . fileName . endsWith ( ".lua" ) ;
130+ const dependencyPath = requiredFromLuaFile ? luaRequireToPath ( dependency ) : dependency ;
131+
132+ if ( requiredFromLuaFile && isNodeModulesFile ( requiringFile . fileName ) ) {
133+ // If requiring file is in lua module, try to resolve sibling in that file first
134+ const resolvedNodeModulesFile = this . resolveLuaDependencyPathFromNodeModules ( requiringFile , dependencyPath ) ;
135+ if ( resolvedNodeModulesFile ) return resolvedNodeModulesFile ;
136+ }
137+
129138 // Check if the import is relative
130139 const isRelative = [ "/" , "./" , "../" ] . some ( p => dependency . startsWith ( p ) ) ;
131140
@@ -134,21 +143,13 @@ class ResolutionContext {
134143 const relativeTo = isRelative ? fileDirectory : this . options . baseUrl ?? fileDirectory ;
135144
136145 // Check if file is a file in the project
137- const resolvedPath = path . join ( relativeTo , dependency ) ;
146+ const resolvedPath = path . join ( relativeTo , dependencyPath ) ;
138147 const fileFromPath = this . getFileFromPath ( resolvedPath ) ;
139148 if ( fileFromPath ) return fileFromPath ;
140149
141- // Check if this is a sibling of a required lua file
142- if ( requiringFile . fileName . endsWith ( ".lua" ) ) {
143- const luaFilePath = resolveLuaPath ( fileDirectory , dependency , this . emitHost ) ;
144- if ( luaFilePath ) {
145- return luaFilePath ;
146- }
147- }
148-
149150 // Not a TS file in our project sources, use resolver to check if we can find dependency
150151 try {
151- const resolveResult = resolver . resolveSync ( { } , fileDirectory , dependency ) ;
152+ const resolveResult = resolver . resolveSync ( { } , fileDirectory , dependencyPath ) ;
152153 if ( resolveResult ) return resolveResult ;
153154 } catch ( e ) {
154155 // resolveSync errors if it fails to resolve
@@ -157,6 +158,30 @@ class ResolutionContext {
157158 return undefined ;
158159 }
159160
161+ private resolveLuaDependencyPathFromNodeModules (
162+ requiringFile : ProcessedFile ,
163+ dependency : string
164+ ) : string | undefined {
165+ // We don't know for sure where the lua root is, so guess it is at package root
166+ const splitPath = path . normalize ( requiringFile . fileName ) . split ( path . sep ) ;
167+ let packageRootIndex = splitPath . lastIndexOf ( "node_modules" ) + 2 ;
168+ let packageRoot = splitPath . slice ( 0 , packageRootIndex ) . join ( path . sep ) ;
169+
170+ while ( packageRootIndex < splitPath . length ) {
171+ // Try to find lua file relative to currently guessed Lua root
172+ const resolvedPath = path . join ( packageRoot , dependency ) ;
173+ const fileFromPath = this . getFileFromPath ( resolvedPath ) ;
174+ if ( fileFromPath ) {
175+ return fileFromPath ;
176+ } else {
177+ // Did not find file at current root, try again one directory deeper
178+ packageRoot = path . join ( packageRoot , splitPath [ packageRootIndex ++ ] ) ;
179+ }
180+ }
181+
182+ return undefined ;
183+ }
184+
160185 // value is false if already searched but not found
161186 private pathToFile = new Map < string , string | false > ( ) ;
162187
@@ -215,39 +240,6 @@ export function resolveDependencies(program: ts.Program, files: ProcessedFile[],
215240 return { resolvedFiles : [ ...resolutionContext . resolvedFiles . values ( ) ] , diagnostics : resolutionContext . diagnostics } ;
216241}
217242
218- function resolveLuaPath ( fromPath : string , dependency : string , emitHost : EmitHost ) {
219- const splitDependency = dependency . split ( "." ) ;
220- if ( splitDependency . length === 1 ) {
221- // If dependency has just one part (the file), look for a lua file with that name
222- const fileDirectory = walkUpFileTreeUntil ( fromPath , dir =>
223- emitHost . fileExists ( path . join ( dir , dependency ) + ".lua" )
224- ) ;
225- if ( fileDirectory ) {
226- return path . join ( fileDirectory , dependency ) + ".lua" ;
227- }
228- } else {
229- // If dependency has multiple parts, look for the first directory of the require path, which must be in the lua root
230- const luaRoot = walkUpFileTreeUntil ( fromPath , dir =>
231- emitHost . directoryExists ( path . join ( dir , splitDependency [ 0 ] ) )
232- ) ;
233- if ( luaRoot ) {
234- return path . join ( luaRoot , dependency . replace ( / \. / g, path . sep ) ) + ".lua" ;
235- }
236- }
237- }
238-
239- function walkUpFileTreeUntil ( fromDirectory : string , predicate : ( dir : string ) => boolean ) {
240- const currentDir = path . normalize ( fromDirectory ) . split ( path . sep ) ;
241- while ( currentDir . length > 0 ) {
242- const dir = currentDir . join ( path . sep ) ;
243- if ( predicate ( dir ) ) {
244- return dir ;
245- }
246- currentDir . pop ( ) ;
247- }
248- return undefined ;
249- }
250-
251243function shouldRewriteRequires ( resolvedDependency : string , program : ts . Program ) {
252244 return ! isBuildModeLibrary ( program ) || ! isNodeModulesFile ( resolvedDependency ) ;
253245}
@@ -344,3 +336,7 @@ function fallbackResolve(required: string, sourceRootDir: string, fileDir: strin
344336 . join ( path . sep )
345337 ) ;
346338}
339+
340+ function luaRequireToPath ( requirePath : string ) : string {
341+ return requirePath . replace ( / \. / g, path . sep ) ;
342+ }
0 commit comments