@@ -52,7 +52,6 @@ import {
5252 getRelativePathFromDirectory ,
5353 getResolveJsonModule ,
5454 getRootLength ,
55- hasJSFileExtension ,
5655 hasProperty ,
5756 hasTrailingDirectorySeparator ,
5857 hostGetCanonicalFileName ,
@@ -99,7 +98,6 @@ import {
9998 startsWith ,
10099 stringContains ,
101100 supportedDeclarationExtensions ,
102- supportedTSExtensionsFlat ,
103101 supportedTSImplementationExtensions ,
104102 toPath ,
105103 tryExtractTSExtension ,
@@ -151,7 +149,7 @@ function removeIgnoredPackageId(r: Resolved | undefined): PathAndExtension | und
151149/** Result of trying to resolve a module. */
152150interface Resolved {
153151 path : string ;
154- extension : Extension ;
152+ extension : string ;
155153 packageId : PackageId | undefined ;
156154 /**
157155 * When the resolved is not created from cache, the value is
@@ -170,7 +168,7 @@ interface Resolved {
170168interface PathAndExtension {
171169 path : string ;
172170 // (Use a different name than `extension` to make sure Resolved isn't assignable to PathAndExtension.)
173- ext : Extension ;
171+ ext : string ;
174172 resolvedUsingTsExtension : boolean | undefined ;
175173}
176174
@@ -1856,21 +1854,21 @@ function loadModuleFromFile(extensions: Extensions, candidate: string, onlyRecor
18561854}
18571855
18581856function loadModuleFromFileNoImplicitExtensions ( extensions : Extensions , candidate : string , onlyRecordFailures : boolean , state : ModuleResolutionState ) : PathAndExtension | undefined {
1859- // If that didn't work, try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one;
1860- // e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
1861- if ( hasJSFileExtension ( candidate ) ||
1862- extensions & Extensions . Json && fileExtensionIs ( candidate , Extension . Json ) ||
1863- extensions & ( Extensions . TypeScript | Extensions . Declaration )
1864- && moduleResolutionSupportsResolvingTsExtensions ( state . compilerOptions )
1865- && fileExtensionIsOneOf ( candidate , supportedTSExtensionsFlat )
1866- ) {
1867- const extensionless = removeFileExtension ( candidate ) ;
1868- const extension = candidate . substring ( extensionless . length ) ;
1869- if ( state . traceEnabled ) {
1870- trace ( state . host , Diagnostics . File_name_0_has_a_1_extension_stripping_it , candidate , extension ) ;
1871- }
1872- return tryAddingExtensions ( extensionless , extensions , extension , onlyRecordFailures , state ) ;
1857+ const filename = getBaseFileName ( candidate ) ;
1858+ if ( filename . indexOf ( "." ) === - 1 ) {
1859+ return undefined ; // extensionless import, no lookups performed, since we don't support extensionless files
1860+ }
1861+ let extensionless = removeFileExtension ( candidate ) ;
1862+ if ( extensionless === candidate ) {
1863+ // Once TS native extensions are handled, handle arbitrary extensions for declaration file mapping
1864+ extensionless = candidate . substring ( 0 , candidate . lastIndexOf ( "." ) ) ;
1865+ }
1866+
1867+ const extension = candidate . substring ( extensionless . length ) ;
1868+ if ( state . traceEnabled ) {
1869+ trace ( state . host , Diagnostics . File_name_0_has_a_1_extension_stripping_it , candidate , extension ) ;
18731870 }
1871+ return tryAddingExtensions ( extensionless , extensions , extension , onlyRecordFailures , state ) ;
18741872}
18751873
18761874/**
@@ -1909,47 +1907,46 @@ function tryAddingExtensions(candidate: string, extensions: Extensions, original
19091907 case Extension . Mjs :
19101908 case Extension . Mts :
19111909 case Extension . Dmts :
1912- return extensions & Extensions . TypeScript && tryExtension ( Extension . Mts )
1913- || extensions & Extensions . Declaration && tryExtension ( Extension . Dmts )
1910+ return extensions & Extensions . TypeScript && tryExtension ( Extension . Mts , originalExtension === Extension . Mts || originalExtension === Extension . Dmts )
1911+ || extensions & Extensions . Declaration && tryExtension ( Extension . Dmts , originalExtension === Extension . Mts || originalExtension === Extension . Dmts )
19141912 || extensions & Extensions . JavaScript && tryExtension ( Extension . Mjs )
19151913 || undefined ;
19161914 case Extension . Cjs :
19171915 case Extension . Cts :
19181916 case Extension . Dcts :
1919- return extensions & Extensions . TypeScript && tryExtension ( Extension . Cts )
1920- || extensions & Extensions . Declaration && tryExtension ( Extension . Dcts )
1917+ return extensions & Extensions . TypeScript && tryExtension ( Extension . Cts , originalExtension === Extension . Cts || originalExtension === Extension . Dcts )
1918+ || extensions & Extensions . Declaration && tryExtension ( Extension . Dcts , originalExtension === Extension . Cts || originalExtension === Extension . Dcts )
19211919 || extensions & Extensions . JavaScript && tryExtension ( Extension . Cjs )
19221920 || undefined ;
19231921 case Extension . Json :
1924- const originalCandidate = candidate ;
1925- if ( extensions & Extensions . Declaration ) {
1926- candidate += Extension . Json ;
1927- const result = tryExtension ( Extension . Dts ) ;
1928- if ( result ) return result ;
1929- }
1930- if ( extensions & Extensions . Json ) {
1931- candidate = originalCandidate ;
1932- const result = tryExtension ( Extension . Json ) ;
1933- if ( result ) return result ;
1934- }
1935- return undefined ;
1936- case Extension . Ts :
1922+ return extensions & Extensions . Declaration && tryExtension ( ".d.json.ts" )
1923+ || extensions & Extensions . Json && tryExtension ( Extension . Json )
1924+ || undefined ;
19371925 case Extension . Tsx :
1926+ case Extension . Jsx :
1927+ // basically idendical to the ts/js case below, but prefers matching tsx and jsx files exactly before falling back to the ts or js file path
1928+ // (historically, we disallow having both a a.ts and a.tsx file in the same compilation, since their outputs clash)
1929+ // TODO: We should probably error if `"./a.tsx"` resolved to `"./a.ts"`, right?
1930+ return extensions & Extensions . TypeScript && ( tryExtension ( Extension . Tsx , originalExtension === Extension . Tsx ) || tryExtension ( Extension . Ts , originalExtension === Extension . Tsx ) )
1931+ || extensions & Extensions . Declaration && tryExtension ( Extension . Dts , originalExtension === Extension . Tsx )
1932+ || extensions & Extensions . JavaScript && ( tryExtension ( Extension . Jsx ) || tryExtension ( Extension . Js ) )
1933+ || undefined ;
1934+ case Extension . Ts :
19381935 case Extension . Dts :
1939- if ( moduleResolutionSupportsResolvingTsExtensions ( state . compilerOptions ) && extensionIsOk ( extensions , originalExtension ) ) {
1940- return tryExtension ( originalExtension , /*resolvedUsingTsExtension*/ true ) ;
1941- }
1942- // falls through
1943- default :
1944- return extensions & Extensions . TypeScript && ( tryExtension ( Extension . Ts ) || tryExtension ( Extension . Tsx ) )
1945- || extensions & Extensions . Declaration && tryExtension ( Extension . Dts )
1936+ case Extension . Js :
1937+ case "" :
1938+ return extensions & Extensions . TypeScript && ( tryExtension ( Extension . Ts , originalExtension === Extension . Ts || originalExtension === Extension . Dts ) || tryExtension ( Extension . Tsx , originalExtension === Extension . Ts || originalExtension === Extension . Dts ) )
1939+ || extensions & Extensions . Declaration && tryExtension ( Extension . Dts , originalExtension === Extension . Ts || originalExtension === Extension . Dts )
19461940 || extensions & Extensions . JavaScript && ( tryExtension ( Extension . Js ) || tryExtension ( Extension . Jsx ) )
19471941 || state . isConfigLookup && tryExtension ( Extension . Json )
19481942 || undefined ;
1943+ default :
1944+ return extensions & Extensions . Declaration && ! isDeclarationFileName ( candidate + originalExtension ) && tryExtension ( `.d${ originalExtension } .ts` )
1945+ || undefined ;
19491946
19501947 }
19511948
1952- function tryExtension ( ext : Extension , resolvedUsingTsExtension ?: boolean ) : PathAndExtension | undefined {
1949+ function tryExtension ( ext : string , resolvedUsingTsExtension ?: boolean ) : PathAndExtension | undefined {
19531950 const path = tryFile ( candidate + ext , onlyRecordFailures , state ) ;
19541951 return path === undefined ? undefined : { path, ext, resolvedUsingTsExtension } ;
19551952 }
@@ -2984,14 +2981,10 @@ export function classicNameResolver(moduleName: string, containingFile: string,
29842981 }
29852982}
29862983
2987- export function moduleResolutionSupportsResolvingTsExtensions ( compilerOptions : CompilerOptions ) {
2988- return getEmitModuleResolutionKind ( compilerOptions ) === ModuleResolutionKind . Bundler ;
2989- }
2990-
29912984// Program errors validate that `noEmit` or `emitDeclarationOnly` is also set,
29922985// so this function doesn't check them to avoid propagating errors.
29932986export function shouldAllowImportingTsExtension ( compilerOptions : CompilerOptions , fromFileName ?: string ) {
2994- return moduleResolutionSupportsResolvingTsExtensions ( compilerOptions ) && (
2987+ return getEmitModuleResolutionKind ( compilerOptions ) === ModuleResolutionKind . Bundler && (
29952988 ! ! compilerOptions . allowImportingTsExtensions ||
29962989 fromFileName && isDeclarationFileName ( fromFileName ) ) ;
29972990}
0 commit comments