Skip to content

Commit 39d1f20

Browse files
committed
feat!: Allow ts paths aliases (#84)
1 parent aa800e5 commit 39d1f20

File tree

14 files changed

+253
-120
lines changed

14 files changed

+253
-120
lines changed

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

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ const visitImport = require("../util/visit-import")
1515
* @returns {string[]} File extensions.
1616
*/
1717
function getExistingExtensions(filePath) {
18-
const basename = path.basename(filePath, path.extname(filePath))
18+
const directory = path.dirname(filePath)
19+
const extension = path.extname(filePath)
20+
const basename = path.basename(filePath, extension)
21+
1922
try {
2023
return fs
21-
.readdirSync(path.dirname(filePath))
22-
.filter(
23-
filename =>
24-
path.basename(filename, path.extname(filename)) === basename
25-
)
24+
.readdirSync(directory)
25+
.filter(filename => filename.startsWith(`${basename}.`))
2626
.map(filename => path.extname(filename))
2727
} catch (_error) {
2828
return []
@@ -74,47 +74,56 @@ module.exports = {
7474
}
7575

7676
// Get extension.
77-
const originalExt = path.extname(name)
78-
const existingExts = getExistingExtensions(filePath)
79-
const ext = path.extname(filePath) || existingExts.join(" or ")
80-
const style = overrideStyle[ext] || defaultStyle
77+
const currentExt = path.extname(name)
78+
const actualExt = path.extname(filePath)
79+
const style = overrideStyle[actualExt] || defaultStyle
80+
81+
const expectedExt = mapTypescriptExtension(
82+
context,
83+
filePath,
84+
actualExt
85+
)
8186

8287
// Verify.
83-
if (style === "always" && ext !== originalExt) {
84-
const fileExtensionToAdd = mapTypescriptExtension(
85-
context,
86-
filePath,
87-
ext
88-
)
88+
if (style === "always" && currentExt !== expectedExt) {
8989
context.report({
9090
node,
9191
messageId: "requireExt",
92-
data: { ext: fileExtensionToAdd },
92+
data: { ext: expectedExt },
9393
fix(fixer) {
94-
if (existingExts.length !== 1) {
95-
return null
96-
}
9794
const index = node.range[1] - 1
9895
return fixer.insertTextBeforeRange(
9996
[index, index],
100-
fileExtensionToAdd
97+
expectedExt
10198
)
10299
},
103100
})
104-
} else if (style === "never" && ext === originalExt) {
101+
}
102+
103+
if (
104+
style === "never" &&
105+
currentExt !== "" &&
106+
expectedExt !== "" &&
107+
currentExt === expectedExt
108+
) {
109+
const otherExtensions = getExistingExtensions(filePath)
110+
111+
let fix = fixer => {
112+
const index = name.lastIndexOf(currentExt)
113+
const start = node.range[0] + 1 + index
114+
const end = start + currentExt.length
115+
return fixer.removeRange([start, end])
116+
}
117+
118+
if (otherExtensions.length > 1) {
119+
fix = undefined
120+
}
121+
105122
context.report({
106123
node,
107124
messageId: "forbidExt",
108-
data: { ext },
109-
fix(fixer) {
110-
if (existingExts.length !== 1) {
111-
return null
112-
}
113-
const index = name.lastIndexOf(ext)
114-
const start = node.range[0] + 1 + index
115-
const end = start + ext.length
116-
return fixer.removeRange([start, end])
117-
},
125+
data: { ext: currentExt },
126+
fix,
118127
})
119128
}
120129
}

lib/util/check-existence.js

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@ const getAllowModules = require("./get-allow-modules")
1010
const isTypescript = require("./is-typescript")
1111
const mapTypescriptExtension = require("../util/map-typescript-extension")
1212

13+
/**
14+
* Reports a missing file from ImportTarget
15+
* @param {RuleContext} context - A context to report.
16+
* @param {import('../util/import-target.js')} target - A list of target information to check.
17+
* @returns {void}
18+
*/
19+
function markMissing(context, target) {
20+
context.report({
21+
node: target.node,
22+
loc: target.node.loc,
23+
messageId: "notFound",
24+
data: target,
25+
})
26+
}
27+
1328
/**
1429
* Checks whether or not each requirement target exists.
1530
*
@@ -24,36 +39,43 @@ exports.checkExistence = function checkExistence(context, targets) {
2439
const allowed = new Set(getAllowModules(context))
2540

2641
for (const target of targets) {
27-
const missingModule =
42+
if (
2843
target.moduleName != null &&
2944
!allowed.has(target.moduleName) &&
3045
target.filePath == null
46+
) {
47+
markMissing(context, target)
48+
continue
49+
}
50+
51+
if (target.moduleName != null) {
52+
continue
53+
}
54+
55+
let missingFile =
56+
target.filePath == null ? false : !exists(target.filePath)
3157

32-
let missingFile = target.moduleName == null && !exists(target.filePath)
3358
if (missingFile && isTypescript(context)) {
3459
const parsed = path.parse(target.filePath)
60+
const pathWithoutExt = path.resolve(parsed.dir, parsed.name)
61+
3562
const reversedExts = mapTypescriptExtension(
3663
context,
3764
target.filePath,
3865
parsed.ext,
3966
true
4067
)
4168
const reversedPaths = reversedExts.map(
42-
reversedExt =>
43-
path.resolve(parsed.dir, parsed.name) + reversedExt
69+
reversedExt => pathWithoutExt + reversedExt
4470
)
4571
missingFile = reversedPaths.every(
4672
reversedPath =>
4773
target.moduleName == null && !exists(reversedPath)
4874
)
4975
}
50-
if (missingModule || missingFile) {
51-
context.report({
52-
node: target.node,
53-
loc: target.node.loc,
54-
messageId: "notFound",
55-
data: target,
56-
})
76+
77+
if (missingFile) {
78+
markMissing(context, target)
5779
}
5880
}
5981
}

lib/util/get-try-extensions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55
"use strict"
66

7-
const DEFAULT_VALUE = Object.freeze([".js", ".json", ".node"])
7+
const DEFAULT_VALUE = Object.freeze([".js", ".json", ".node", ".mjs", ".cjs"])
88

99
/**
1010
* Gets `tryExtensions` property from a given option object.

lib/util/get-tsconfig.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function getTSConfig(filename) {
1717
* Attempts to get the ExtensionMap from the tsconfig of a given file.
1818
*
1919
* @param {string} filename - The path to the file we need to find the tsconfig.json of
20-
* @returns {import("get-tsconfig").TsConfigResult}
20+
* @returns {import("get-tsconfig").TsConfigResult | null}
2121
*/
2222
function getTSConfigForFile(filename) {
2323
return getTsconfig(filename, "tsconfig.json", fsCache)

lib/util/get-typescript-extension-map.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,14 @@ const tsConfigMapping = {
3232
* @property {Record<string, string[]>} backward Convert from javascript to typescript
3333
*/
3434

35+
/**
36+
* @param {Record<string, string>} typescriptExtensionMap A forward extension mapping
37+
* @returns {ExtensionMap}
38+
*/
3539
function normalise(typescriptExtensionMap) {
3640
const forward = {}
3741
const backward = {}
42+
3843
for (const [typescript, javascript] of typescriptExtensionMap) {
3944
forward[typescript] = javascript
4045
if (!typescript) {
@@ -43,6 +48,7 @@ function normalise(typescriptExtensionMap) {
4348
backward[javascript] ??= []
4449
backward[javascript].push(typescript)
4550
}
51+
4652
return { forward, backward }
4753
}
4854

@@ -109,7 +115,7 @@ function getFromTSConfigFromFile(filename) {
109115
* 8. This returns `PRESERVE_MAPPING`.
110116
*
111117
* @param {import("eslint").Rule.RuleContext} context - The rule context.
112-
* @returns {string[]} A list of extensions.
118+
* @returns {ExtensionMap} A list of extensions.
113119
*/
114120
module.exports = function getTypescriptExtensionMap(context) {
115121
return (

0 commit comments

Comments
 (0)