From 4ae382db909d8931226fe0a9544e463dbe3bcc8b Mon Sep 17 00:00:00 2001 From: Sebastian Good <2230835+scagood@users.noreply.github.com> Date: Mon, 11 Sep 2023 00:10:05 +0300 Subject: [PATCH] feat: Add "tsconfigPath" option to ts ext mapping --- docs/rules/no-missing-import.md | 4 +++ docs/rules/no-missing-require.md | 4 +++ lib/rules/no-missing-import.js | 2 ++ lib/rules/no-missing-require.js | 2 ++ lib/util/get-tsconfig.js | 31 ++++++++++++++++ lib/util/get-typescript-extension-map.js | 45 +++++++++++++++++------- tests/lib/rules/no-missing-import.js | 36 ++++++++++++++++++- tests/lib/rules/no-missing-require.js | 36 ++++++++++++++++++- 8 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 lib/util/get-tsconfig.js diff --git a/docs/rules/no-missing-import.md b/docs/rules/no-missing-import.md index 8ffb7960..3138b22d 100644 --- a/docs/rules/no-missing-import.md +++ b/docs/rules/no-missing-import.md @@ -93,6 +93,10 @@ Default is: ] ``` +#### tsconfigPath + +Adds the ability to specify the tsconfig used by the typescriptExtensionMap tool. + ### Shared Settings The following options can be set by [shared settings](http://eslint.org/docs/user-guide/configuring.html#adding-shared-settings). diff --git a/docs/rules/no-missing-require.md b/docs/rules/no-missing-require.md index d3309942..b0fe2d90 100644 --- a/docs/rules/no-missing-require.md +++ b/docs/rules/no-missing-require.md @@ -106,6 +106,10 @@ Default is: ] ``` +#### tsconfigPath + +Adds the ability to specify the tsconfig used by the typescriptExtensionMap tool. + ### Shared Settings The following options can be set by [shared settings](http://eslint.org/docs/user-guide/configuring.html#adding-shared-settings). diff --git a/lib/rules/no-missing-import.js b/lib/rules/no-missing-import.js index 3b780475..2c73fc3f 100644 --- a/lib/rules/no-missing-import.js +++ b/lib/rules/no-missing-import.js @@ -7,6 +7,7 @@ const { checkExistence, messages } = require("../util/check-existence") const getAllowModules = require("../util/get-allow-modules") const getResolvePaths = require("../util/get-resolve-paths") +const getTSConfig = require("../util/get-tsconfig") const getTypescriptExtensionMap = require("../util/get-typescript-extension-map") const visitImport = require("../util/visit-import") @@ -28,6 +29,7 @@ module.exports = { allowModules: getAllowModules.schema, resolvePaths: getResolvePaths.schema, typescriptExtensionMap: getTypescriptExtensionMap.schema, + tsconfigPath: getTSConfig.schema, }, additionalProperties: false, }, diff --git a/lib/rules/no-missing-require.js b/lib/rules/no-missing-require.js index 7566fa49..11a36636 100644 --- a/lib/rules/no-missing-require.js +++ b/lib/rules/no-missing-require.js @@ -7,6 +7,7 @@ const { checkExistence, messages } = require("../util/check-existence") const getAllowModules = require("../util/get-allow-modules") const getResolvePaths = require("../util/get-resolve-paths") +const getTSConfig = require("../util/get-tsconfig") const getTryExtensions = require("../util/get-try-extensions") const getTypescriptExtensionMap = require("../util/get-typescript-extension-map") const visitRequire = require("../util/visit-require") @@ -30,6 +31,7 @@ module.exports = { tryExtensions: getTryExtensions.schema, resolvePaths: getResolvePaths.schema, typescriptExtensionMap: getTypescriptExtensionMap.schema, + tsconfigPath: getTSConfig.schema, }, additionalProperties: false, }, diff --git a/lib/util/get-tsconfig.js b/lib/util/get-tsconfig.js new file mode 100644 index 00000000..cbc202f0 --- /dev/null +++ b/lib/util/get-tsconfig.js @@ -0,0 +1,31 @@ +"use strict" + +const { getTsconfig, parseTsconfig } = require("get-tsconfig") +const fsCache = new Map() + +/** + * Attempts to get the ExtensionMap from the tsconfig given the path to the tsconfig file. + * + * @param {string} filename - The path to the tsconfig.json file + * @returns {import("get-tsconfig").TsConfigJsonResolved} + */ +function getTSConfig(filename) { + return parseTsconfig(filename, fsCache) +} + +/** + * Attempts to get the ExtensionMap from the tsconfig of a given file. + * + * @param {string} filename - The path to the file we need to find the tsconfig.json of + * @returns {import("get-tsconfig").TsConfigResult} + */ +function getTSConfigForFile(filename) { + return getTsconfig(filename, "tsconfig.json", fsCache) +} + +module.exports = { + getTSConfig, + getTSConfigForFile, +} + +module.exports.schema = { type: "string" } diff --git a/lib/util/get-typescript-extension-map.js b/lib/util/get-typescript-extension-map.js index a913cae2..ccdb2feb 100644 --- a/lib/util/get-typescript-extension-map.js +++ b/lib/util/get-typescript-extension-map.js @@ -1,7 +1,6 @@ "use strict" -const { getTsconfig } = require("get-tsconfig") -const fsCache = new Map() +const { getTSConfig, getTSConfigForFile } = require("./get-tsconfig") const DEFAULT_MAPPING = normalise([ ["", ".js"], @@ -47,6 +46,22 @@ function normalise(typescriptExtensionMap) { return { forward, backward } } +/** + * Attempts to get the ExtensionMap from the resolved tsconfig. + * + * @param {import("get-tsconfig").TsConfigJsonResolved} [tsconfig] - The resolved tsconfig + * @returns {ExtensionMap} The `typescriptExtensionMap` value, or `null`. + */ +function getMappingFromTSConfig(tsconfig) { + const jsx = tsconfig?.compilerOptions?.jsx + + if ({}.hasOwnProperty.call(tsConfigMapping, jsx)) { + return tsConfigMapping[jsx] + } + + return null +} + /** * Gets `typescriptExtensionMap` property from a given option object. * @@ -64,6 +79,10 @@ function get(option) { return normalise(option.typescriptExtensionMap) } + if (option?.tsconfigPath) { + return getMappingFromTSConfig(getTSConfig(option?.tsconfigPath)) + } + return null } @@ -73,15 +92,8 @@ function get(option) { * @param {string} filename - The filename we're getting from * @returns {ExtensionMap} The `typescriptExtensionMap` value, or `null`. */ -function getFromTSConfig(filename) { - const tsconfig = getTsconfig(filename, "tsconfig.json", fsCache) - - const jsx = tsconfig?.config?.compilerOptions?.jsx - if ({}.hasOwnProperty.call(tsConfigMapping, jsx)) { - return tsConfigMapping[jsx] - } - - return null +function getFromTSConfigFromFile(filename) { + return getMappingFromTSConfig(getTSConfigForFile(filename)?.config) } /** @@ -103,8 +115,15 @@ module.exports = function getTypescriptExtensionMap(context) { return ( get(context.options?.[0]) || get(context.settings?.n ?? context.settings?.node) || - getFromTSConfig( - context.physicalFilename ?? context.getPhysicalFilename() + getFromTSConfigFromFile( + // eslint ^8 + context.physicalFilename ?? + // eslint ^7.28 (deprecated ^8) + context.getPhysicalFilename?.() ?? + // eslint ^8 (if physicalFilename undefined) + context.filename ?? + // eslint ^7 (deprecated ^8) + context.getFilename?.() ) || PRESERVE_MAPPING ) diff --git a/tests/lib/rules/no-missing-import.js b/tests/lib/rules/no-missing-import.js index 96240d75..af2dc125 100644 --- a/tests/lib/rules/no-missing-import.js +++ b/tests/lib/rules/no-missing-import.js @@ -214,7 +214,41 @@ ruleTester.run("no-missing-import", rule, { env: { node: true }, }, - // tsx from config + // explicit tsx from config + { + // name: "options[0] - preserve - e.tsx as e.jsx", + filename: fixture("ts-react/test.tsx"), + code: "import e from './e.jsx';", + options: [{ tsconfigPath: fixture("ts-preserve/tsconfig.json") }], + env: { node: true }, + }, + { + // name: "options[0] - react - e.tsx as e.js", + filename: fixture("ts-preserve/test.tsx"), + code: "import e from './e.js';", + options: [{ tsconfigPath: fixture("ts-react/tsconfig.json") }], + env: { node: true }, + }, + { + // name: "settings.node - preserve - e.tsx as e.jsx", + filename: fixture("ts-react/test.tsx"), + code: "import e from './e.jsx';", + settings: { + node: { tsconfigPath: fixture("ts-preserve/tsconfig.json") }, + }, + env: { node: true }, + }, + { + // name: "settings.node - react - e.tsx as e.js", + filename: fixture("ts-preserve/test.tsx"), + code: "import e from './e.js';", + settings: { + node: { tsconfigPath: fixture("ts-react/tsconfig.json") }, + }, + env: { node: true }, + }, + + // implicit tsx from config { // name: "tsconfig - jsx: react - e.tsx as e.js", filename: fixture("ts-react/test.tsx"), diff --git a/tests/lib/rules/no-missing-require.js b/tests/lib/rules/no-missing-require.js index 95a3f8c9..74381147 100644 --- a/tests/lib/rules/no-missing-require.js +++ b/tests/lib/rules/no-missing-require.js @@ -307,7 +307,41 @@ ruleTester.run("no-missing-require", rule, { env: { node: true }, }, - // tsx from config + // explicit tsx from config + { + // name: "options[0] - preserve - e.tsx as e.jsx", + filename: fixture("ts-react/test.tsx"), + code: "require('./e.jsx');", + options: [{ tsconfigPath: fixture("ts-preserve/tsconfig.json") }], + env: { node: true }, + }, + { + // name: "options[0] - react - e.tsx as e.js", + filename: fixture("ts-preserve/test.tsx"), + code: "require('./e.js');", + options: [{ tsconfigPath: fixture("ts-react/tsconfig.json") }], + env: { node: true }, + }, + { + // name: "settings.node - preserve - e.tsx as e.jsx", + filename: fixture("ts-react/test.tsx"), + code: "require('./e.jsx');", + settings: { + node: { tsconfigPath: fixture("ts-preserve/tsconfig.json") }, + }, + env: { node: true }, + }, + { + // name: "settings.node - react - e.tsx as e.js", + filename: fixture("ts-preserve/test.tsx"), + code: "require('./e.js');", + settings: { + node: { tsconfigPath: fixture("ts-react/tsconfig.json") }, + }, + env: { node: true }, + }, + + // implicit tsx from config { filename: fixture("ts-react/test.tsx"), code: "require('./e.js');",