@@ -13,12 +13,15 @@ const visitImport = require("../util/visit-import")
1313/**
1414 * Get all file extensions of the files which have the same basename.
1515 * @param {string } filePath The path to the original file to check.
16+ * @param {string } [basename] Optional basename to use instead of deriving from filePath.
1617 * @returns {string[] } File extensions.
1718 */
18- function getExistingExtensions ( filePath ) {
19+ function getExistingExtensions ( filePath , basename ) {
1920 const directory = path . dirname ( filePath )
20- const extension = path . extname ( filePath )
21- const basename = path . basename ( filePath , extension )
21+ if ( basename == null ) {
22+ const extension = path . extname ( filePath )
23+ basename = path . basename ( filePath , extension )
24+ }
2225
2326 try {
2427 return fs
@@ -115,17 +118,63 @@ module.exports = {
115118
116119 // Get extension.
117120 const currentExt = path . extname ( name )
118- const actualExt = path . extname ( filePath )
121+ let actualExt = path . extname ( filePath )
122+ let actualFilePath = filePath
123+
124+ // If the file doesn't exist (or is a directory), the resolver may have returned
125+ // a fallback path. In this case, path.extname may return a "fake" extension
126+ // (e.g., ".client" for "utils.client" when the actual file is "utils.client.ts").
127+ // We need to search for the actual file using the full basename from the import.
128+ const fileExists =
129+ fs . existsSync ( filePath ) && fs . statSync ( filePath ) . isFile ( )
130+ if ( actualExt !== "" && ! fileExists ) {
131+ // Use the full basename (e.g., "utils.client") since what path.extname
132+ // thinks is an extension (e.g., ".client") may not be a real file extension
133+ const importBasename = path . basename ( name )
134+ const extensions = getExistingExtensions (
135+ filePath ,
136+ importBasename
137+ )
138+ // Find the preferred extension based on tryExtensions order
139+ const preferred = tryExtensions . find ( ext =>
140+ extensions . includes ( ext )
141+ )
142+ const foundExt = preferred ?? extensions [ 0 ]
143+ if ( foundExt ) {
144+ actualExt = foundExt
145+ actualFilePath = path . join (
146+ path . dirname ( filePath ) ,
147+ `${ importBasename } ${ actualExt } `
148+ )
149+ }
150+ }
151+
152+ let isDirectoryImport = false
153+
154+ // Check for directory imports. This handles both:
155+ // 1. Normal case: "./my-folder" -> "./my-folder/index.js"
156+ // 2. Dot-in-name case: "./my-things.client" -> "./my-things.client/index.js"
157+ // where path.extname incorrectly sees ".client" as an extension
158+ const isDirectory =
159+ fs . existsSync ( filePath ) && fs . statSync ( filePath ) . isDirectory ( )
160+ if ( isDirectory ) {
161+ const indexExt = getIndexExtension ( filePath , tryExtensions )
162+ if ( indexExt ) {
163+ isDirectoryImport = true
164+ actualExt = indexExt
165+ actualFilePath = path . join ( filePath , `index${ indexExt } ` )
166+ }
167+ }
168+
119169 const style = overrideStyle [ actualExt ] || defaultStyle
120170
121171 let expectedExt = convertTsExtensionToJs (
122172 context ,
123- filePath ,
173+ actualFilePath ,
124174 actualExt
125175 )
126- let isDirectoryImport = false
127176
128- if ( currentExt === "" && actualExt === "" ) {
177+ if ( currentExt === "" && actualExt === "" && ! isDirectoryImport ) {
129178 const indexExt = getIndexExtension ( filePath , tryExtensions )
130179 if ( indexExt ) {
131180 isDirectoryImport = true
0 commit comments