Skip to content

Commit c95d9cd

Browse files
committed
Add proper file extensions when importing a typescript file from a typescript file
Cherry-picked from https://github.com/giladgd/eslint-plugin-node/tree/dev/giladgd/fixImportExtentionFixingInTypeScript and lingering PR mysticatea#303 Thanks @giladgd
1 parent 88b4d95 commit c95d9cd

File tree

3 files changed

+46
-2
lines changed

3 files changed

+46
-2
lines changed

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

+38-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ const visitImport = require("../util/visit-import")
1010
const packageNamePattern = /^(?:@[^/\\]+[/\\])?[^/\\]+$/u
1111
const corePackageOverridePattern =
1212
/^(?:assert|async_hooks|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|http2|https|inspector|module|net|os|path|perf_hooks|process|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|trace_events|tty|url|util|v8|vm|worker_threads|zlib)[/\\]$/u
13+
const typescriptFileExtensionsMapping = {
14+
".ts": ".js",
15+
".cts": ".cjs",
16+
".mts": ".mjs",
17+
}
1318

1419
/**
1520
* Get all file extensions of the files which have the same basename.
@@ -31,6 +36,27 @@ function getExistingExtensions(filePath) {
3136
}
3237
}
3338

39+
/**
40+
* Get the file extension that should be added in an import statement,
41+
* based on the given file extension of the referenced file.
42+
*
43+
* For example, in typescript, when referencing another typescript from a typescript file,
44+
* a .js extension should be used instead of the original .ts extension of the referenced file.
45+
* @param {string} referencedFileExt The original file extension of the referenced file.
46+
* @param {string} referencingFileExt The original file extension of the file the contains the import statement.
47+
* @returns {string} The file extension to append to the import statement.
48+
*/
49+
function getFileExtensionToAdd(referencedFileExt, referencingFileExt) {
50+
if (
51+
referencingFileExt in typescriptFileExtensionsMapping &&
52+
referencedFileExt in typescriptFileExtensionsMapping
53+
) {
54+
return typescriptFileExtensionsMapping[referencedFileExt]
55+
}
56+
57+
return referencedFileExt
58+
}
59+
3460
module.exports = {
3561
meta: {
3662
docs: {
@@ -85,16 +111,26 @@ module.exports = {
85111

86112
// Verify.
87113
if (style === "always" && ext !== originalExt) {
114+
const referencingFileExt = path.extname(
115+
context.getPhysicalFilename()
116+
)
117+
const fileExtensionToAdd = getFileExtensionToAdd(
118+
ext,
119+
referencingFileExt
120+
)
88121
context.report({
89122
node,
90123
messageId: "requireExt",
91-
data: { ext },
124+
data: { ext: fileExtensionToAdd },
92125
fix(fixer) {
93126
if (existingExts.length !== 1) {
94127
return null
95128
}
96129
const index = node.range[1] - 1
97-
return fixer.insertTextBeforeRange([index, index], ext)
130+
return fixer.insertTextBeforeRange(
131+
[index, index],
132+
fileExtensionToAdd
133+
)
98134
},
99135
})
100136
} else if (style === "never" && ext === originalExt) {

tests/fixtures/file-extension-in-import/d.ts

Whitespace-only changes.

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

+8
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ new RuleTester({
6969
filename: fixture("test.js"),
7070
code: "import './c.mjs'",
7171
},
72+
{
73+
filename: fixture("test.js"),
74+
code: "import './d.js'",
75+
},
76+
{
77+
filename: fixture("test.ts"),
78+
code: "import './d.js'",
79+
},
7280
{
7381
filename: fixture("test.js"),
7482
code: "import './a.js'",

0 commit comments

Comments
 (0)