Skip to content

Commit 612332b

Browse files
authored
fix(resolve): support resolving TS files by JS extension specifiers in JS files (#18889)
1 parent 2c2d521 commit 612332b

File tree

6 files changed

+29
-49
lines changed

6 files changed

+29
-49
lines changed

packages/vite/src/node/optimizer/scan.ts

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,6 @@ export function devToScanEnvironment(
105105
} as unknown as ScanEnvironment
106106
}
107107

108-
type ResolveIdOptions = Omit<
109-
Parameters<EnvironmentPluginContainer['resolveId']>[2],
110-
'environment'
111-
>
112-
113108
const debug = createDebugger('vite:deps')
114109

115110
const htmlTypesRE = /\.(html|vue|svelte|astro|imba)$/
@@ -383,27 +378,19 @@ function esbuildScanPlugin(
383378
async function resolveId(
384379
id: string,
385380
importer?: string,
386-
options?: ResolveIdOptions,
387381
): Promise<PartialResolvedId | null> {
388382
return environment.pluginContainer.resolveId(
389383
id,
390384
importer && normalizePath(importer),
391-
{
392-
...options,
393-
scan: true,
394-
},
385+
{ scan: true },
395386
)
396387
}
397-
const resolve = async (
398-
id: string,
399-
importer?: string,
400-
options?: ResolveIdOptions,
401-
) => {
388+
const resolve = async (id: string, importer?: string) => {
402389
const key = id + (importer && path.dirname(importer))
403390
if (seen.has(key)) {
404391
return seen.get(key)
405392
}
406-
const resolved = await resolveId(id, importer, options)
393+
const resolved = await resolveId(id, importer)
407394
const res = resolved?.id
408395
seen.set(key, res)
409396
return res
@@ -633,18 +620,14 @@ function esbuildScanPlugin(
633620
// avoid matching windows volume
634621
filter: /^[\w@][^:]/,
635622
},
636-
async ({ path: id, importer, pluginData }) => {
623+
async ({ path: id, importer }) => {
637624
if (moduleListContains(exclude, id)) {
638625
return externalUnlessEntry({ path: id })
639626
}
640627
if (depImports[id]) {
641628
return externalUnlessEntry({ path: id })
642629
}
643-
const resolved = await resolve(id, importer, {
644-
custom: {
645-
depScan: { loader: pluginData?.htmlType?.loader },
646-
},
647-
})
630+
const resolved = await resolve(id, importer)
648631
if (resolved) {
649632
if (shouldExternalizeDep(resolved, id)) {
650633
return externalUnlessEntry({ path: id })
@@ -706,13 +689,9 @@ function esbuildScanPlugin(
706689
{
707690
filter: /.*/,
708691
},
709-
async ({ path: id, importer, pluginData }) => {
692+
async ({ path: id, importer }) => {
710693
// use vite resolver to support urls and omitted extensions
711-
const resolved = await resolve(id, importer, {
712-
custom: {
713-
depScan: { loader: pluginData?.htmlType?.loader },
714-
},
715-
})
694+
const resolved = await resolve(id, importer)
716695
if (resolved) {
717696
if (
718697
shouldExternalizeDep(resolved, id) ||

packages/vite/src/node/plugins/resolve.ts

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import {
2929
isNonDriveRelativeAbsolutePath,
3030
isObject,
3131
isOptimizable,
32-
isTsRequest,
3332
normalizePath,
3433
safeRealpathSync,
3534
tryStatSync,
@@ -125,10 +124,7 @@ interface ResolvePluginOptions {
125124
tryPrefix?: string
126125
preferRelative?: boolean
127126
isRequire?: boolean
128-
// #3040
129-
// when the importer is a ts module,
130-
// if the specifier requests a non-existent `.js/jsx/mjs/cjs` file,
131-
// should also try import from `.ts/tsx/mts/cts` source file as fallback.
127+
/** @deprecated */
132128
isFromTsImporter?: boolean
133129
// True when resolving during the scan phase to discover dependencies
134130
scan?: boolean
@@ -241,18 +237,6 @@ export function resolvePlugin(
241237
}
242238
}
243239

244-
if (importer) {
245-
if (
246-
isTsRequest(importer) ||
247-
resolveOpts.custom?.depScan?.loader?.startsWith('ts')
248-
) {
249-
options.isFromTsImporter = true
250-
} else {
251-
const moduleLang = this.getModuleInfo(importer)?.meta.vite?.lang
252-
options.isFromTsImporter = moduleLang && isTsRequest(`.${moduleLang}`)
253-
}
254-
}
255-
256240
let res: string | PartialResolvedId | undefined
257241

258242
// resolve pre-bundled deps requests, these could be resolved by
@@ -616,7 +600,7 @@ function tryCleanFsResolve(
616600
let res: string | undefined
617601

618602
// If path.dirname is a valid directory, try extensions and ts resolution logic
619-
const possibleJsToTs = options.isFromTsImporter && isPossibleTsOutput(file)
603+
const possibleJsToTs = isPossibleTsOutput(file)
620604
if (possibleJsToTs || options.extensions.length || tryPrefix) {
621605
const dirPath = path.dirname(file)
622606
if (isDirectory(dirPath)) {

packages/vite/src/node/utils.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,6 @@ export const isJSRequest = (url: string): boolean => {
327327
return false
328328
}
329329

330-
const knownTsRE = /\.(?:ts|mts|cts|tsx)(?:$|\?)/
331-
export const isTsRequest = (url: string): boolean => knownTsRE.test(url)
332-
333330
const importQueryRE = /(\?|&)import=?(?:&|$)/
334331
const directRequestRE = /(\?|&)direct=?(?:&|$)/
335332
const internalPrefixes = [

playground/resolve/__tests__/resolve.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ test('a ts module can import another ts module using its corresponding js file n
111111
expect(await page.textContent('.ts-extension')).toMatch('[success]')
112112
})
113113

114+
test('a js module can import another ts module using its corresponding js file name', async () => {
115+
expect(await page.textContent('.js-ts-extension')).toMatch('[success]')
116+
})
117+
114118
test('filename with dot', async () => {
115119
expect(await page.textContent('.dot')).toMatch('[success]')
116120
})

playground/resolve/index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ <h2>
113113
</h2>
114114
<p class="cjs-extension">fail</p>
115115

116+
<h2>A js module can import TS modules using its corresponding js file name</h2>
117+
<p class="js-ts-extension">fail</p>
118+
116119
<h2>Resolve file name containing dot</h2>
117120
<p class="dot">fail</p>
118121

@@ -304,6 +307,9 @@ <h2>utf8-bom-package</h2>
304307
import { msgMjs as tsMjsExtensionWithQueryMsg } from './ts-extension?query=1'
305308
text('.mjs-extension-with-query', tsMjsExtensionWithQueryMsg)
306309

310+
import { msg as jsTsExtensionMsg } from './ts-extension/index-js.js'
311+
text('.js-ts-extension', jsTsExtensionMsg)
312+
307313
// filename with dot
308314
import { bar } from './util/bar.util'
309315
text('.dot', bar())
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { msg as msgJs } from './hello.js'
2+
import { msgJsx } from './hellojsx.jsx'
3+
import { msgTsx } from './hellotsx.js'
4+
import { msgCjs } from './hellocjs.cjs'
5+
import { msgMjs } from './hellomjs.mjs'
6+
7+
export const msg =
8+
msgJs && msgJsx && msgTsx && msgCjs && msgMjs
9+
? '[success] use .js / .jsx / .cjs / .mjs extension to import a TS modules'
10+
: '[fail]'

0 commit comments

Comments
 (0)