Skip to content

Commit 754a1a6

Browse files
authored
fix(file-extension-in-import): handle directory index imports (#499)
1 parent 047301a commit 754a1a6

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

lib/rules/file-extension-in-import.js

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
const path = require("path")
88
const fs = require("fs")
99
const { convertTsExtensionToJs } = require("../util/map-typescript-extension")
10+
const getTryExtensions = require("../util/get-try-extensions")
1011
const visitImport = require("../util/visit-import")
1112

1213
/**
@@ -29,6 +30,34 @@ function getExistingExtensions(filePath) {
2930
}
3031
}
3132

33+
/**
34+
* Get the extension of an index file in the given directory.
35+
* @param {string} directoryPath The directory path to check.
36+
* @param {string[]} tryExtensions Ordered extension preferences.
37+
* @returns {string | null} The index file extension or null if none.
38+
*/
39+
function getIndexExtension(directoryPath, tryExtensions) {
40+
try {
41+
if (fs.statSync(directoryPath).isDirectory() === false) {
42+
return null
43+
}
44+
} catch {
45+
return null
46+
}
47+
48+
const existing = getExistingExtensions(path.join(directoryPath, "index"))
49+
if (existing.length === 0) {
50+
return null
51+
}
52+
53+
const preferred = tryExtensions.find(ext => existing.includes(ext))
54+
if (preferred) {
55+
return preferred
56+
}
57+
58+
return existing[0] || null
59+
}
60+
3261
/**
3362
* @typedef {[
3463
* ("always" | "never")?,
@@ -69,6 +98,7 @@ module.exports = {
6998
}
7099
const defaultStyle = context.options[0] || "always"
71100
const overrideStyle = context.options[1] || {}
101+
const tryExtensions = getTryExtensions(context, 1)
72102

73103
/**
74104
* @param {import("../util/import-target.js")} target
@@ -88,11 +118,24 @@ module.exports = {
88118
const actualExt = path.extname(filePath)
89119
const style = overrideStyle[actualExt] || defaultStyle
90120

91-
const expectedExt = convertTsExtensionToJs(
121+
let expectedExt = convertTsExtensionToJs(
92122
context,
93123
filePath,
94124
actualExt
95125
)
126+
let isDirectoryImport = false
127+
128+
if (currentExt === "" && actualExt === "") {
129+
const indexExt = getIndexExtension(filePath, tryExtensions)
130+
if (indexExt) {
131+
isDirectoryImport = true
132+
expectedExt = convertTsExtensionToJs(
133+
context,
134+
path.join(filePath, `index${indexExt}`),
135+
indexExt
136+
)
137+
}
138+
}
96139

97140
// Verify.
98141
if (style === "always" && currentExt !== expectedExt) {
@@ -106,6 +149,13 @@ module.exports = {
106149
}
107150

108151
const index = node.range[1] - 1
152+
if (isDirectoryImport) {
153+
const needsSlash = /[/\\]$/.test(name) ? "" : "/"
154+
return fixer.insertTextBeforeRange(
155+
[index, index],
156+
`${needsSlash}index${expectedExt}`
157+
)
158+
}
109159
return fixer.insertTextBeforeRange(
110160
[index, index],
111161
expectedExt
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const util = () => {}

tests/lib/rules/file-extension-in-import.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,18 @@ new RuleTester({
222222
output: "import './d.js'",
223223
errors: [{ messageId: "requireExt", data: { ext: ".js" } }],
224224
},
225+
{
226+
filename: fixture("test.js"),
227+
code: "import { util } from './my-folder'",
228+
output: "import { util } from './my-folder/index.js'",
229+
errors: [{ messageId: "requireExt", data: { ext: ".js" } }],
230+
},
231+
{
232+
filename: fixture("test.js"),
233+
code: "import { util } from './my-folder/'",
234+
output: "import { util } from './my-folder/index.js'",
235+
errors: [{ messageId: "requireExt", data: { ext: ".js" } }],
236+
},
225237
{
226238
filename: fixture("test.js"),
227239
code: "import './b'",

0 commit comments

Comments
 (0)