Skip to content

Commit 4d57419

Browse files
committed
add support of jsx extension
1 parent d44aa44 commit 4d57419

File tree

7 files changed

+58
-23
lines changed

7 files changed

+58
-23
lines changed

src/compiler/moduleSpecifiers.ts

+22-16
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace ts.moduleSpecifiers {
44
export interface ModuleSpecifierPreferences {
55
importModuleSpecifierPreference?: "relative" | "non-relative";
6-
includingJsExtensionOnAutoImports?: boolean;
6+
includeExtensionInImports?: Extension.Js | Extension.Jsx;
77
}
88

99
// Note: fromSourceFile is just for usesJsExtensionOnImports
@@ -38,39 +38,39 @@ namespace ts.moduleSpecifiers {
3838

3939
interface Info {
4040
readonly moduleResolutionKind: ModuleResolutionKind;
41-
readonly addJsExtension: boolean;
41+
readonly addExtension: Extension.Js | Extension.Jsx | undefined;
4242
readonly getCanonicalFileName: GetCanonicalFileName;
4343
readonly sourceDirectory: string;
4444
}
4545
// importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path
4646
function getInfo(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: string, host: ModuleSpecifierResolutionHost, preferences: ModuleSpecifierPreferences): Info {
4747
const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions);
48-
const addJsExtension = usesJsExtensionOnImports(importingSourceFile, preferences);
48+
const addExtension = usesExtensionOnImports(importingSourceFile, preferences);
4949
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : true);
5050
const sourceDirectory = getDirectoryPath(importingSourceFileName);
51-
return { moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory };
51+
return { moduleResolutionKind, addExtension, getCanonicalFileName, sourceDirectory };
5252
}
5353

5454
function getGlobalModuleSpecifier(
5555
moduleFileName: string,
56-
{ addJsExtension, getCanonicalFileName, sourceDirectory }: Info,
56+
{ addExtension, getCanonicalFileName, sourceDirectory }: Info,
5757
host: ModuleSpecifierResolutionHost,
5858
compilerOptions: CompilerOptions,
5959
) {
60-
return tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addJsExtension)
60+
return tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addExtension)
6161
|| tryGetModuleNameAsNodeModule(compilerOptions, moduleFileName, host, getCanonicalFileName, sourceDirectory)
6262
|| compilerOptions.rootDirs && tryGetModuleNameFromRootDirs(compilerOptions.rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName);
6363
}
6464

6565
function getLocalModuleSpecifiers(
6666
moduleFileName: string,
67-
{ moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory }: Info,
67+
{ moduleResolutionKind, addExtension, getCanonicalFileName, sourceDirectory }: Info,
6868
compilerOptions: CompilerOptions,
6969
preferences: ModuleSpecifierPreferences,
7070
) {
7171
const { baseUrl, paths } = compilerOptions;
7272

73-
const relativePath = removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), moduleResolutionKind, addJsExtension);
73+
const relativePath = removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), moduleResolutionKind, addExtension);
7474
if (!baseUrl || preferences.importModuleSpecifierPreference === "relative") {
7575
return [relativePath];
7676
}
@@ -80,7 +80,7 @@ namespace ts.moduleSpecifiers {
8080
return [relativePath];
8181
}
8282

83-
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, moduleResolutionKind, addJsExtension);
83+
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, moduleResolutionKind, addExtension);
8484
if (paths) {
8585
const fromPaths = tryGetModuleNameFromPaths(removeFileExtension(relativeToBaseUrl), importRelativeToBaseUrl, paths);
8686
if (fromPaths) {
@@ -130,8 +130,14 @@ namespace ts.moduleSpecifiers {
130130
return relativeFirst ? [relativePath, importRelativeToBaseUrl] : [importRelativeToBaseUrl, relativePath];
131131
}
132132

133-
function usesJsExtensionOnImports({ imports }: SourceFile, preferences: ModuleSpecifierPreferences): boolean {
134-
return firstDefined(imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || !!preferences.includingJsExtensionOnAutoImports;
133+
function tryGetFileExtension (text: string) {
134+
return pathIsRelative(text) && fileExtensionIsOneOf(text, [Extension.Js, Extension.Jsx]) ? (
135+
fileExtensionIs(text, Extension.Js) ? Extension.Js : Extension.Jsx
136+
) : undefined;
137+
}
138+
139+
function usesExtensionOnImports({ imports }: SourceFile, preferences: ModuleSpecifierPreferences): Extension.Js | Extension.Jsx | undefined {
140+
return preferences.includeExtensionInImports === undefined ? firstDefined(imports, ({ text }) => tryGetFileExtension(text)) : preferences.includeExtensionInImports;
135141
}
136142

137143
function discoverProbableSymlinks(files: ReadonlyArray<SourceFile>, getCanonicalFileName: (file: string) => string, host: ModuleSpecifierResolutionHost) {
@@ -245,14 +251,14 @@ namespace ts.moduleSpecifiers {
245251
host: GetEffectiveTypeRootsHost,
246252
getCanonicalFileName: (file: string) => string,
247253
moduleFileName: string,
248-
addJsExtension: boolean,
254+
addExtension: Extension.Js | Extension.Jsx | undefined,
249255
): string | undefined {
250256
const roots = getEffectiveTypeRoots(options, host);
251257
return firstDefined(roots, unNormalizedTypeRoot => {
252258
const typeRoot = toPath(unNormalizedTypeRoot, /*basePath*/ undefined, getCanonicalFileName);
253259
if (startsWith(moduleFileName, typeRoot)) {
254260
// For a type definition, we can strip `/index` even with classic resolution.
255-
return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), ModuleResolutionKind.NodeJs, addJsExtension);
261+
return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), ModuleResolutionKind.NodeJs, addExtension);
256262
}
257263
});
258264
}
@@ -397,10 +403,10 @@ namespace ts.moduleSpecifiers {
397403
});
398404
}
399405

400-
function removeExtensionAndIndexPostFix(fileName: string, moduleResolutionKind: ModuleResolutionKind, addJsExtension: boolean): string {
406+
function removeExtensionAndIndexPostFix(fileName: string, moduleResolutionKind: ModuleResolutionKind, addExtension: Extension.Js | Extension.Jsx | undefined): string {
401407
const noExtension = removeFileExtension(fileName);
402-
return addJsExtension
403-
? noExtension + ".js"
408+
return addExtension
409+
? noExtension + addExtension
404410
: moduleResolutionKind === ModuleResolutionKind.NodeJs
405411
? removeSuffix(noExtension, "/index")
406412
: noExtension;

src/server/protocol.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2733,7 +2733,7 @@ namespace ts.server.protocol {
27332733
readonly includeCompletionsWithInsertText?: boolean;
27342734
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
27352735
readonly allowTextChangesInNewFiles?: boolean;
2736-
readonly includingJsExtensionOnAutoImports?: boolean;
2736+
readonly includeExtensionInImports?: Extension.Js | Extension.Jsx;
27372737
}
27382738

27392739
export interface CompilerOptions {

src/services/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ namespace ts {
240240
readonly includeCompletionsWithInsertText?: boolean;
241241
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
242242
readonly allowTextChangesInNewFiles?: boolean;
243-
readonly includingJsExtensionOnAutoImports?: boolean;
243+
readonly includeExtensionInImports?: Extension.Js | Extension.Jsx;
244244
}
245245
/* @internal */
246246
export const defaultPreferences: UserPreferences = {};

tests/baselines/reference/api/tsserverlibrary.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -9252,6 +9252,7 @@ declare namespace ts {
92529252
declare namespace ts.moduleSpecifiers {
92539253
interface ModuleSpecifierPreferences {
92549254
importModuleSpecifierPreference?: "relative" | "non-relative";
9255+
includeExtensionInImports?: Extension.Js | Extension.Jsx;
92559256
}
92569257
function getModuleSpecifier(compilerOptions: CompilerOptions, fromSourceFile: SourceFile, fromSourceFileName: string, toFileName: string, host: ModuleSpecifierResolutionHost, preferences?: ModuleSpecifierPreferences): string;
92579258
function getModuleSpecifiers(moduleSymbol: Symbol, compilerOptions: CompilerOptions, importingSourceFile: SourceFile, host: ModuleSpecifierResolutionHost, files: ReadonlyArray<SourceFile>, preferences: ModuleSpecifierPreferences): ReadonlyArray<ReadonlyArray<string>>;
@@ -9941,6 +9942,7 @@ declare namespace ts {
99419942
readonly includeCompletionsWithInsertText?: boolean;
99429943
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
99439944
readonly allowTextChangesInNewFiles?: boolean;
9945+
readonly includeExtensionInImports?: Extension.Js | Extension.Jsx;
99449946
}
99459947
const defaultPreferences: UserPreferences;
99469948
interface LanguageService {
@@ -13203,6 +13205,7 @@ declare namespace ts.server.protocol {
1320313205
readonly includeCompletionsWithInsertText?: boolean;
1320413206
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
1320513207
readonly allowTextChangesInNewFiles?: boolean;
13208+
readonly includeExtensionInImports?: Extension.Js | Extension.Jsx;
1320613209
}
1320713210
interface CompilerOptions {
1320813211
allowJs?: boolean;

tests/baselines/reference/api/typescript.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4731,6 +4731,7 @@ declare namespace ts {
47314731
readonly includeCompletionsWithInsertText?: boolean;
47324732
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
47334733
readonly allowTextChangesInNewFiles?: boolean;
4734+
readonly includeExtensionInImports?: Extension.Js | Extension.Jsx;
47344735
}
47354736
interface LanguageService {
47364737
cleanupSemanticCache(): void;

tests/cases/fourslash/fourslash.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ declare namespace FourSlashInterface {
531531
includeCompletionsForModuleExports?: boolean;
532532
includeInsertTextCompletions?: boolean;
533533
importModuleSpecifierPreference?: "relative" | "non-relative";
534-
includingJsExtensionOnAutoImports?: boolean
534+
includeExtensionInImports?: '.js' | '.jsx'
535535
}
536536
interface CompletionsAtOptions extends UserPreferences {
537537
triggerCharacter?: string;

tests/cases/fourslash/importNameCodeFix_jsExtensionPreference.ts

+29-4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@
3232
// @Filename: /i.ts
3333
////h;
3434

35+
// @Filename: /j.jsx
36+
////export function j() {}
37+
38+
// @Filename: /k.js
39+
////j;
40+
41+
// @Filename: /l.js
42+
////j;
43+
3544
goTo.file("/b.js");
3645
verify.importFixAtPosition([
3746
`import { a } from "./a";
@@ -43,7 +52,7 @@ verify.importFixAtPosition([
4352
`import { a } from "./a.js";
4453
4554
a;`], /* errorCode */ undefined, {
46-
includingJsExtensionOnAutoImports: true
55+
includeExtensionInImports: '.js'
4756
});
4857

4958
goTo.file("/d.ts");
@@ -57,21 +66,37 @@ verify.importFixAtPosition([
5766
`import { a } from "./a.js";
5867
5968
a;`], /* errorCode */ undefined, {
60-
includingJsExtensionOnAutoImports: true
69+
includeExtensionInImports: '.js'
6170
});
6271

6372
goTo.file("/g.ts");
6473
verify.importFixAtPosition([
6574
`import { f } from "./f.js";
6675
6776
f;`], /* errorCode */ undefined, {
68-
includingJsExtensionOnAutoImports: true
77+
includeExtensionInImports: '.js'
6978
});
7079

7180
goTo.file("/i.ts");
7281
verify.importFixAtPosition([
7382
`import h from "./h.js";
7483
7584
h;`], /* errorCode */ undefined, {
76-
includingJsExtensionOnAutoImports: true
85+
includeExtensionInImports: '.js'
86+
});
87+
88+
goTo.file("/k.js");
89+
verify.importFixAtPosition([
90+
`import { j } from "./j.js";
91+
92+
j;`], /* errorCode */ undefined, {
93+
includeExtensionInImports: '.js'
94+
});
95+
96+
goTo.file("/l.js");
97+
verify.importFixAtPosition([
98+
`import { j } from "./j.jsx";
99+
100+
j;`], /* errorCode */ undefined, {
101+
includeExtensionInImports: '.jsx'
77102
});

0 commit comments

Comments
 (0)