Skip to content

Commit d9a7882

Browse files
committed
feat: Allow for "allowImportingTsExtensions" (#134)
1 parent 5f74824 commit d9a7882

File tree

10 files changed

+98
-31
lines changed

10 files changed

+98
-31
lines changed

lib/util/get-try-extensions.js

+39-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,26 @@
44
*/
55
"use strict"
66

7-
const DEFAULT_VALUE = Object.freeze([".js", ".json", ".node", ".mjs", ".cjs"])
7+
const { getTSConfigForContext } = require("./get-tsconfig")
8+
const isTypescript = require("./is-typescript")
9+
10+
const DEFAULT_JS_VALUE = Object.freeze([
11+
".js",
12+
".json",
13+
".node",
14+
".mjs",
15+
".cjs",
16+
])
17+
const DEFAULT_TS_VALUE = Object.freeze([
18+
".js",
19+
".ts",
20+
".mjs",
21+
".mts",
22+
".cjs",
23+
".cts",
24+
".json",
25+
".node",
26+
])
827

928
/**
1029
* Gets `tryExtensions` property from a given option object.
@@ -13,7 +32,7 @@ const DEFAULT_VALUE = Object.freeze([".js", ".json", ".node", ".mjs", ".cjs"])
1332
* @returns {string[]|null} The `tryExtensions` value, or `null`.
1433
*/
1534
function get(option) {
16-
if (option && option.tryExtensions && Array.isArray(option.tryExtensions)) {
35+
if (Array.isArray(option?.tryExtensions)) {
1736
return option.tryExtensions.map(String)
1837
}
1938
return null
@@ -24,19 +43,29 @@ function get(option) {
2443
*
2544
* 1. This checks `options` property, then returns it if exists.
2645
* 2. This checks `settings.n` | `settings.node` property, then returns it if exists.
27-
* 3. This returns `[".js", ".json", ".node"]`.
46+
* 3. This returns `[".js", ".json", ".node", ".mjs", ".cjs"]`.
2847
*
2948
* @param {RuleContext} context - The rule context.
3049
* @returns {string[]} A list of extensions.
3150
*/
3251
module.exports = function getTryExtensions(context, optionIndex = 0) {
33-
return (
34-
get(context.options && context.options[optionIndex]) ||
35-
get(
36-
context.settings && (context.settings.n || context.settings.node)
37-
) ||
38-
DEFAULT_VALUE
39-
)
52+
const configured =
53+
get(context.options?.[optionIndex]) ??
54+
get(context.settings?.n) ??
55+
get(context.settings?.node)
56+
57+
if (configured != null) {
58+
return configured
59+
}
60+
61+
if (isTypescript(context)) {
62+
const tsconfig = getTSConfigForContext(context)
63+
if (tsconfig?.config?.compilerOptions?.allowImportingTsExtensions) {
64+
return DEFAULT_TS_VALUE
65+
}
66+
}
67+
68+
return DEFAULT_JS_VALUE
4069
}
4170

4271
module.exports.schema = {

lib/util/get-tsconfig.js

+18
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,27 @@ function getTSConfigForFile(filename) {
2323
return getTsconfig(filename, "tsconfig.json", fsCache)
2424
}
2525

26+
/**
27+
* Attempts to get the ExtensionMap from the tsconfig of a given file.
28+
*
29+
* @param {import('eslint').Rule.RuleContext} context - The current eslint context
30+
* @returns {import("get-tsconfig").TsConfigResult | null}
31+
*/
32+
function getTSConfigForContext(context) {
33+
// TODO: remove context.get(PhysicalFilename|Filename) when dropping eslint < v10
34+
const filename =
35+
context.physicalFilename ??
36+
context.getPhysicalFilename?.() ??
37+
context.filename ??
38+
context.getFilename?.()
39+
40+
return getTSConfigForFile(filename)
41+
}
42+
2643
module.exports = {
2744
getTSConfig,
2845
getTSConfigForFile,
46+
getTSConfigForContext,
2947
}
3048

3149
module.exports.schema = { type: "string" }

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

+5-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use strict"
22

3-
const { getTSConfig, getTSConfigForFile } = require("./get-tsconfig")
3+
const { getTSConfig, getTSConfigForContext } = require("./get-tsconfig")
44

55
const DEFAULT_MAPPING = normalise([
66
["", ".js"],
@@ -95,11 +95,11 @@ function get(option) {
9595
/**
9696
* Attempts to get the ExtensionMap from the tsconfig of a given file.
9797
*
98-
* @param {string} filename - The filename we're getting from
98+
* @param {import('eslint').Rule.RuleContext} context - The current file context
9999
* @returns {ExtensionMap} The `typescriptExtensionMap` value, or `null`.
100100
*/
101-
function getFromTSConfigFromFile(filename) {
102-
return getMappingFromTSConfig(getTSConfigForFile(filename)?.config)
101+
function getFromTSConfigFromFile(context) {
102+
return getMappingFromTSConfig(getTSConfigForContext(context)?.config)
103103
}
104104

105105
/**
@@ -118,15 +118,10 @@ function getFromTSConfigFromFile(filename) {
118118
* @returns {ExtensionMap} A list of extensions.
119119
*/
120120
module.exports = function getTypescriptExtensionMap(context) {
121-
const filename =
122-
context.physicalFilename ??
123-
context.getPhysicalFilename?.() ??
124-
context.filename ??
125-
context.getFilename?.() // TODO: remove context.get(PhysicalFilename|Filename) when dropping eslint < v10
126121
return (
127122
get(context.options?.[0]) ||
128123
get(context.settings?.n ?? context.settings?.node) ||
129-
getFromTSConfigFromFile(filename) ||
124+
getFromTSConfigFromFile(context) ||
130125
PRESERVE_MAPPING
131126
)
132127
}

lib/util/import-target.js

+2-11
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const isBuiltin = require("is-builtin-module")
1111
const resolver = require("enhanced-resolve")
1212

1313
const isTypescript = require("./is-typescript")
14-
const { getTSConfigForFile } = require("./get-tsconfig.js")
14+
const { getTSConfigForContext } = require("./get-tsconfig.js")
1515
const getTypescriptExtensionMap = require("./get-typescript-extension-map")
1616

1717
function removeTrailWildcard(input) {
@@ -28,16 +28,7 @@ function removeTrailWildcard(input) {
2828
* @returns {import('enhanced-resolve').ResolveOptions['alias'] | undefined}
2929
*/
3030
function getTSConfigAliases(context) {
31-
const tsConfig = getTSConfigForFile(
32-
// eslint ^8
33-
context.physicalFilename ??
34-
// eslint ^7.28 (deprecated ^8)
35-
context.getPhysicalFilename?.() ??
36-
// eslint ^8 (if physicalFilename undefined)
37-
context.filename ??
38-
// eslint ^7 (deprecated ^8)
39-
context.getFilename?.()
40-
)
31+
const tsConfig = getTSConfigForContext(context)
4132

4233
const paths = tsConfig?.config?.compilerOptions?.paths
4334

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

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"compilerOptions": {
3+
"noEmit": true,
4+
"allowImportingTsExtensions": true
5+
}
6+
}

tests/fixtures/no-missing/ts-allow-extension/file.ts

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"compilerOptions": {
3+
"noEmit": true,
4+
"allowImportingTsExtensions": true
5+
}
6+
}

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

+11
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,17 @@ new RuleTester({
197197
env: { node: true },
198198
settings: { node: { typescriptExtensionMap: tsReactExtensionMap } },
199199
},
200+
201+
{
202+
filename: fixture("ts-allow-extension/test.ts"),
203+
code: "require('./file.js');",
204+
env: { node: true },
205+
},
206+
{
207+
filename: fixture("ts-allow-extension/test.ts"),
208+
code: "require('./file.ts');",
209+
env: { node: true },
210+
},
200211
],
201212
invalid: [
202213
{

tests/lib/rules/no-missing-import.js

+11
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,17 @@ ruleTester.run("no-missing-import", rule, {
310310
env: { node: true },
311311
},
312312

313+
{
314+
filename: fixture("ts-allow-extension/test.ts"),
315+
code: "import './file.js';",
316+
env: { node: true },
317+
},
318+
{
319+
filename: fixture("ts-allow-extension/test.ts"),
320+
code: "import './file.ts';",
321+
env: { node: true },
322+
},
323+
313324
// import()
314325
...(DynamicImportSupported
315326
? [

0 commit comments

Comments
 (0)