From 72b8cb60475dea83fc426151753358b8c3fe92b4 Mon Sep 17 00:00:00 2001 From: Lee <56801187+leebeydoun@users.noreply.github.com> Date: Wed, 2 Mar 2022 19:07:09 +0000 Subject: [PATCH] fix(resolve): try .tsx extension for .js import from typescript module (#7005) Co-authored-by: Alec Larson <1925840+aleclarson@users.noreply.github.com> --- packages/playground/resolve/index.html | 17 +++++++++ .../resolve/ts-extension/hellojsx.tsx | 1 + .../resolve/ts-extension/hellotsx.tsx | 1 + .../playground/resolve/ts-extension/index.ts | 4 +- .../vite/src/node/__tests__/utils.spec.ts | 38 ++++++++++++++++++- packages/vite/src/node/plugins/resolve.ts | 26 +++++++------ packages/vite/src/node/utils.ts | 10 ++++- 7 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 packages/playground/resolve/ts-extension/hellojsx.tsx create mode 100644 packages/playground/resolve/ts-extension/hellotsx.tsx diff --git a/packages/playground/resolve/index.html b/packages/playground/resolve/index.html index 71699035abdd53..c0569345d86837 100644 --- a/packages/playground/resolve/index.html +++ b/packages/playground/resolve/index.html @@ -41,6 +41,17 @@

fail

+

+ A ts module can import another tsx module using its corresponding jsx file + name +

+

fail

+ +

+ A ts module can import another tsx module using its corresponding js file name +

+

fail

+

Resolve file name containing dot

fail

@@ -137,6 +148,12 @@

resolve package that contains # in path

import { msg as tsExtensionMsg } from './ts-extension' text('.ts-extension', tsExtensionMsg) + import { msgJsx as tsJsxExtensionMsg } from './ts-extension' + text('.jsx-extension', tsJsxExtensionMsg) + + import { msgTsx as tsTsxExtensionMsg } from './ts-extension' + text('.tsx-extension', tsTsxExtensionMsg) + // filename with dot import { bar } from './util/bar.util' text('.dot', bar()) diff --git a/packages/playground/resolve/ts-extension/hellojsx.tsx b/packages/playground/resolve/ts-extension/hellojsx.tsx new file mode 100644 index 00000000000000..a8f610b399b17a --- /dev/null +++ b/packages/playground/resolve/ts-extension/hellojsx.tsx @@ -0,0 +1 @@ +export const msgJsx = '[success] use .jsx extension to import a tsx module' diff --git a/packages/playground/resolve/ts-extension/hellotsx.tsx b/packages/playground/resolve/ts-extension/hellotsx.tsx new file mode 100644 index 00000000000000..b7461ca71ded6c --- /dev/null +++ b/packages/playground/resolve/ts-extension/hellotsx.tsx @@ -0,0 +1 @@ +export const msgTsx = '[success] use .js extension to import a tsx module' diff --git a/packages/playground/resolve/ts-extension/index.ts b/packages/playground/resolve/ts-extension/index.ts index e095619ee4d716..bdb326f8778e64 100644 --- a/packages/playground/resolve/ts-extension/index.ts +++ b/packages/playground/resolve/ts-extension/index.ts @@ -1,3 +1,5 @@ import { msg } from './hello.js' +import { msgJsx } from './hellojsx.jsx' +import { msgTsx } from './hellotsx.js' -export { msg } +export { msg, msgJsx, msgTsx } diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index 3bf82f6b86f84b..26b149cd48b8af 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -1,4 +1,4 @@ -import { injectQuery, isWindows } from '../utils' +import { getPotentialTsSrcPaths, injectQuery, isWindows } from '../utils' if (isWindows) { // this test will work incorrectly on unix systems @@ -38,3 +38,39 @@ test('path with unicode, space, and %', () => { '/usr/vite/東京 %20 hello?direct' ) }) + +test('ts import of file with .js extension', () => { + expect(getPotentialTsSrcPaths('test-file.js')).toEqual([ + 'test-file.ts', + 'test-file.tsx' + ]) +}) + +test('ts import of file with .jsx extension', () => { + expect(getPotentialTsSrcPaths('test-file.jsx')).toEqual(['test-file.tsx']) +}) + +test('ts import of file .mjs,.cjs extension', () => { + expect(getPotentialTsSrcPaths('test-file.cjs')).toEqual([ + 'test-file.cts', + 'test-file.ctsx' + ]) + expect(getPotentialTsSrcPaths('test-file.mjs')).toEqual([ + 'test-file.mts', + 'test-file.mtsx' + ]) +}) + +test('ts import of file with .js before extension', () => { + expect(getPotentialTsSrcPaths('test-file.js.js')).toEqual([ + 'test-file.js.ts', + 'test-file.js.tsx' + ]) +}) + +test('ts import of file with .js and query param', () => { + expect(getPotentialTsSrcPaths('test-file.js.js?lee=123')).toEqual([ + 'test-file.js.ts?lee=123', + 'test-file.js.tsx?lee=123' + ]) +}) diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 03a9026242f428..52258b73a23836 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -27,7 +27,7 @@ import { isFileReadable, isTsRequest, isPossibleTsOutput, - getTsSrcPath + getPotentialTsSrcPaths } from '../utils' import type { ViteDevServer, SSROptions } from '..' import type { PartialResolvedId } from 'rollup' @@ -436,16 +436,20 @@ function tryResolveFile( const tryTsExtension = options.isFromTsImporter && isPossibleTsOutput(file) if (tryTsExtension) { - const tsSrcPath = getTsSrcPath(file) - return tryResolveFile( - tsSrcPath, - postfix, - options, - tryIndex, - targetWeb, - tryPrefix, - skipPackageJson - ) + const tsSrcPaths = getPotentialTsSrcPaths(file) + for (const srcPath of tsSrcPaths) { + const res = tryResolveFile( + srcPath, + postfix, + options, + tryIndex, + targetWeb, + tryPrefix, + skipPackageJson + ) + if (res) return res + } + return } if (tryPrefix) { diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index bc1b8588cb212d..9cba9c7c79b3a3 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -189,8 +189,14 @@ const knownTsOutputRE = /\.(js|mjs|cjs|jsx)$/ export const isTsRequest = (url: string) => knownTsRE.test(cleanUrl(url)) export const isPossibleTsOutput = (url: string) => knownTsOutputRE.test(cleanUrl(url)) -export const getTsSrcPath = (filename: string) => - filename.replace(/\.([cm])?(js)(x?)(\?|$)/, '.$1ts$3') +export function getPotentialTsSrcPaths(filePath: string) { + const [name, type, query = ''] = filePath.split(/(\.(?:[cm]?js|jsx))(\?.*)?$/) + const paths = [name + type.replace('js', 'ts') + query] + if (!type.endsWith('x')) { + paths.push(name + type.replace('js', 'tsx') + query) + } + return paths +} const importQueryRE = /(\?|&)import=?(?:&|$)/ const internalPrefixes = [