diff --git a/packages/playground/resolve/index.html b/packages/playground/resolve/index.html index db0a4bc54f1ad7..100fd60ff653c1 100644 --- a/packages/playground/resolve/index.html +++ b/packages/playground/resolve/index.html @@ -38,6 +38,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

@@ -130,6 +141,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 55afd92df1a8fb..1dacf4ab15a11d 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 3012e6d57e6c65..4fdae99f268bf8 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 = [