Skip to content

Commit 04f3e1a

Browse files
committed
add support of jsx extension
1 parent 4390a7c commit 04f3e1a

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
@@ -2719,7 +2719,7 @@ namespace ts.server.protocol {
27192719
readonly includeCompletionsWithInsertText?: boolean;
27202720
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
27212721
readonly allowTextChangesInNewFiles?: boolean;
2722-
readonly includingJsExtensionOnAutoImports?: boolean;
2722+
readonly includeExtensionInImports?: Extension.Js | Extension.Jsx;
27232723
}
27242724

27252725
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
@@ -9234,6 +9234,7 @@ declare namespace ts {
92349234
declare namespace ts.moduleSpecifiers {
92359235
interface ModuleSpecifierPreferences {
92369236
importModuleSpecifierPreference?: "relative" | "non-relative";
9237+
includeExtensionInImports?: Extension.Js | Extension.Jsx;
92379238
}
92389239
function getModuleSpecifier(compilerOptions: CompilerOptions, fromSourceFile: SourceFile, fromSourceFileName: string, toFileName: string, host: ModuleSpecifierResolutionHost, preferences?: ModuleSpecifierPreferences): string;
92399240
function getModuleSpecifiers(moduleSymbol: Symbol, compilerOptions: CompilerOptions, importingSourceFile: SourceFile, host: ModuleSpecifierResolutionHost, files: ReadonlyArray<SourceFile>, preferences: ModuleSpecifierPreferences): ReadonlyArray<ReadonlyArray<string>>;
@@ -9925,6 +9926,7 @@ declare namespace ts {
99259926
readonly includeCompletionsWithInsertText?: boolean;
99269927
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
99279928
readonly allowTextChangesInNewFiles?: boolean;
9929+
readonly includeExtensionInImports?: Extension.Js | Extension.Jsx;
99289930
}
99299931
const defaultPreferences: UserPreferences;
99309932
interface LanguageService {
@@ -13176,6 +13178,7 @@ declare namespace ts.server.protocol {
1317613178
readonly includeCompletionsWithInsertText?: boolean;
1317713179
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
1317813180
readonly allowTextChangesInNewFiles?: boolean;
13181+
readonly includeExtensionInImports?: Extension.Js | Extension.Jsx;
1317913182
}
1318013183
interface CompilerOptions {
1318113184
allowJs?: boolean;

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

+1
Original file line numberDiff line numberDiff line change
@@ -4717,6 +4717,7 @@ declare namespace ts {
47174717
readonly includeCompletionsWithInsertText?: boolean;
47184718
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
47194719
readonly allowTextChangesInNewFiles?: boolean;
4720+
readonly includeExtensionInImports?: Extension.Js | Extension.Jsx;
47204721
}
47214722
interface LanguageService {
47224723
cleanupSemanticCache(): void;

tests/cases/fourslash/fourslash.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ declare namespace FourSlashInterface {
530530
includeCompletionsForModuleExports?: boolean;
531531
includeInsertTextCompletions?: boolean;
532532
importModuleSpecifierPreference?: "relative" | "non-relative";
533-
includingJsExtensionOnAutoImports?: boolean
533+
includeExtensionInImports?: '.js' | '.jsx'
534534
}
535535
interface CompletionsAtOptions extends UserPreferences {
536536
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)