Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(resolve): try .tsx extension for .js import from typescript module #7005

Merged
merged 7 commits into from Mar 2, 2022
Merged
24 changes: 23 additions & 1 deletion 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
Expand Down Expand Up @@ -38,3 +38,25 @@ 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'])
expect(getPotentialTsSrcPaths('test-file.mjs')).toEqual(['test-file.mts'])
})

test('ts import should not match .js that is not extension', () => {
expect(getPotentialTsSrcPaths('test-file.js.mjs')).toEqual([
'test-file.js.mts'
])
})
26 changes: 15 additions & 11 deletions packages/vite/src/node/plugins/resolve.ts
Expand Up @@ -27,7 +27,7 @@ import {
isFileReadable,
isTsRequest,
isPossibleTsOutput,
getTsSrcPath
getPotentialTsSrcPaths
} from '../utils'
import type { ViteDevServer, SSROptions } from '..'
import type { PartialResolvedId } from 'rollup'
Expand Down Expand Up @@ -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) {
Expand Down
12 changes: 10 additions & 2 deletions packages/vite/src/node/utils.ts
Expand Up @@ -189,8 +189,16 @@ 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 const getPotentialTsSrcPaths = (filename: string) => {
// Special handling for filenames that end in `.js`, since they may actually
// refer to a `.ts` or `.tsx` file in TypeScript.
if (filename.endsWith('.js')) {
const extensionLessFile = filename.slice(0, -3)
return [`${extensionLessFile}.ts`, `${extensionLessFile}.tsx`]
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the filename could potentially contain the ? query parameter too (according to the regex), so checking endsWith could be risky. We would need to handle that, e.g. filename is /foo/bar.js?bla.

I'm also not sure if there's a case for .cjs and .mjs to map to .ctsx and .mtsx. But this PR would be in the next meeting to be discussed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, will think about how to better handle cases with ? after the extension. Also still need to add tests for resolve. Let me know how the meeting goes. I'll be happy to update this PR based on any decisions made there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some tests resolve :)


return [filename.replace(/\.([cm])?(js)(x?)(\?|$)/, '.$1ts$3')]
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really nice, thanks for improving the implementation I came up with.


const importQueryRE = /(\?|&)import=?(?:&|$)/
const internalPrefixes = [
Expand Down