Skip to content

Commit

Permalink
feat: Allow for "allowImportingTsExtensions" (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
scagood authored and aladdin-add committed Jan 9, 2024
1 parent a21f620 commit cb9a965
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 31 deletions.
49 changes: 39 additions & 10 deletions lib/util/get-try-extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,26 @@
*/
"use strict"

const DEFAULT_VALUE = Object.freeze([".js", ".json", ".node", ".mjs", ".cjs"])
const { getTSConfigForContext } = require("./get-tsconfig")
const isTypescript = require("./is-typescript")

const DEFAULT_JS_VALUE = Object.freeze([
".js",
".json",
".node",
".mjs",
".cjs",
])
const DEFAULT_TS_VALUE = Object.freeze([
".js",
".ts",
".mjs",
".mts",
".cjs",
".cts",
".json",
".node",
])

/**
* Gets `tryExtensions` property from a given option object.
Expand All @@ -13,7 +32,7 @@ const DEFAULT_VALUE = Object.freeze([".js", ".json", ".node", ".mjs", ".cjs"])
* @returns {string[]|null} The `tryExtensions` value, or `null`.
*/
function get(option) {
if (option && option.tryExtensions && Array.isArray(option.tryExtensions)) {
if (Array.isArray(option?.tryExtensions)) {
return option.tryExtensions.map(String)
}
return null
Expand All @@ -24,19 +43,29 @@ function get(option) {
*
* 1. This checks `options` property, then returns it if exists.
* 2. This checks `settings.n` | `settings.node` property, then returns it if exists.
* 3. This returns `[".js", ".json", ".node"]`.
* 3. This returns `[".js", ".json", ".node", ".mjs", ".cjs"]`.
*
* @param {RuleContext} context - The rule context.
* @returns {string[]} A list of extensions.
*/
module.exports = function getTryExtensions(context, optionIndex = 0) {
return (
get(context.options && context.options[optionIndex]) ||
get(
context.settings && (context.settings.n || context.settings.node)
) ||
DEFAULT_VALUE
)
const configured =
get(context.options?.[optionIndex]) ??
get(context.settings?.n) ??
get(context.settings?.node)

if (configured != null) {
return configured
}

if (isTypescript(context)) {
const tsconfig = getTSConfigForContext(context)
if (tsconfig?.config?.compilerOptions?.allowImportingTsExtensions) {
return DEFAULT_TS_VALUE
}
}

return DEFAULT_JS_VALUE
}

module.exports.schema = {
Expand Down
18 changes: 18 additions & 0 deletions lib/util/get-tsconfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,27 @@ function getTSConfigForFile(filename) {
return getTsconfig(filename, "tsconfig.json", fsCache)
}

/**
* Attempts to get the ExtensionMap from the tsconfig of a given file.
*
* @param {import('eslint').Rule.RuleContext} context - The current eslint context
* @returns {import("get-tsconfig").TsConfigResult | null}
*/
function getTSConfigForContext(context) {
// TODO: remove context.get(PhysicalFilename|Filename) when dropping eslint < v10
const filename =
context.physicalFilename ??
context.getPhysicalFilename?.() ??
context.filename ??
context.getFilename?.()

return getTSConfigForFile(filename)
}

module.exports = {
getTSConfig,
getTSConfigForFile,
getTSConfigForContext,
}

module.exports.schema = { type: "string" }
15 changes: 5 additions & 10 deletions lib/util/get-typescript-extension-map.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict"

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

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

/**
Expand All @@ -118,15 +118,10 @@ function getFromTSConfigFromFile(filename) {
* @returns {ExtensionMap} A list of extensions.
*/
module.exports = function getTypescriptExtensionMap(context) {
const filename =
context.physicalFilename ??
context.getPhysicalFilename?.() ??
context.filename ??
context.getFilename?.() // TODO: remove context.get(PhysicalFilename|Filename) when dropping eslint < v10
return (
get(context.options?.[0]) ||
get(context.settings?.n ?? context.settings?.node) ||
getFromTSConfigFromFile(filename) ||
getFromTSConfigFromFile(context) ||
PRESERVE_MAPPING
)
}
Expand Down
13 changes: 2 additions & 11 deletions lib/util/import-target.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const isBuiltin = require("is-builtin-module")
const resolver = require("enhanced-resolve")

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

function removeTrailWildcard(input) {
Expand All @@ -28,16 +28,7 @@ function removeTrailWildcard(input) {
* @returns {import('enhanced-resolve').ResolveOptions['alias'] | undefined}
*/
function getTSConfigAliases(context) {
const tsConfig = getTSConfigForFile(
// eslint ^8
context.physicalFilename ??
// eslint ^7.28 (deprecated ^8)
context.getPhysicalFilename?.() ??
// eslint ^8 (if physicalFilename undefined)
context.filename ??
// eslint ^7 (deprecated ^8)
context.getFilename?.()
)
const tsConfig = getTSConfigForContext(context)

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

Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"noEmit": true,
"allowImportingTsExtensions": true
}
}
Empty file.
6 changes: 6 additions & 0 deletions tests/fixtures/no-missing/ts-allow-extension/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"noEmit": true,
"allowImportingTsExtensions": true
}
}
11 changes: 11 additions & 0 deletions tests/lib/rules/file-extension-in-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,17 @@ new RuleTester({
code: "require('./e.js');",
settings: { node: { typescriptExtensionMap: tsReactExtensionMap } },
},

{
filename: fixture("ts-allow-extension/test.ts"),
code: "require('./file.js');",
env: { node: true },
},
{
filename: fixture("ts-allow-extension/test.ts"),
code: "require('./file.ts');",
env: { node: true },
},
],
invalid: [
{
Expand Down
11 changes: 11 additions & 0 deletions tests/lib/rules/no-missing-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,17 @@ ruleTester.run("no-missing-import", rule, {
env: { node: true },
},

{
filename: fixture("ts-allow-extension/test.ts"),
code: "import './file.js';",
env: { node: true },
},
{
filename: fixture("ts-allow-extension/test.ts"),
code: "import './file.ts';",
env: { node: true },
},

// import()
...(DynamicImportSupported
? [
Expand Down

0 comments on commit cb9a965

Please sign in to comment.