Skip to content

Commit d44aa44

Browse files
committed
add support for jsExtension Preference on auto import
1 parent cfe65d1 commit d44aa44

File tree

6 files changed

+88
-7
lines changed

6 files changed

+88
-7
lines changed

src/compiler/moduleSpecifiers.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
namespace ts.moduleSpecifiers {
44
export interface ModuleSpecifierPreferences {
55
importModuleSpecifierPreference?: "relative" | "non-relative";
6+
includingJsExtensionOnAutoImports?: boolean;
67
}
78

89
// Note: fromSourceFile is just for usesJsExtensionOnImports
910
export function getModuleSpecifier(compilerOptions: CompilerOptions, fromSourceFile: SourceFile, fromSourceFileName: string, toFileName: string, host: ModuleSpecifierResolutionHost, preferences: ModuleSpecifierPreferences = {}) {
10-
const info = getInfo(compilerOptions, fromSourceFile, fromSourceFileName, host);
11+
const info = getInfo(compilerOptions, fromSourceFile, fromSourceFileName, host, preferences);
1112
return getGlobalModuleSpecifier(toFileName, info, host, compilerOptions) ||
1213
first(getLocalModuleSpecifiers(toFileName, info, compilerOptions, preferences));
1314
}
@@ -24,7 +25,7 @@ namespace ts.moduleSpecifiers {
2425
const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol);
2526
if (ambient) return [[ambient]];
2627

27-
const info = getInfo(compilerOptions, importingSourceFile, importingSourceFile.path, host);
28+
const info = getInfo(compilerOptions, importingSourceFile, importingSourceFile.path, host, preferences);
2829
if (!files) {
2930
return Debug.fail("Files list must be present to resolve symlinks in specifier resolution");
3031
}
@@ -42,9 +43,9 @@ namespace ts.moduleSpecifiers {
4243
readonly sourceDirectory: string;
4344
}
4445
// importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path
45-
function getInfo(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: string, host: ModuleSpecifierResolutionHost): Info {
46+
function getInfo(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: string, host: ModuleSpecifierResolutionHost, preferences: ModuleSpecifierPreferences): Info {
4647
const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions);
47-
const addJsExtension = usesJsExtensionOnImports(importingSourceFile);
48+
const addJsExtension = usesJsExtensionOnImports(importingSourceFile, preferences);
4849
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : true);
4950
const sourceDirectory = getDirectoryPath(importingSourceFileName);
5051
return { moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory };
@@ -129,8 +130,8 @@ namespace ts.moduleSpecifiers {
129130
return relativeFirst ? [relativePath, importRelativeToBaseUrl] : [importRelativeToBaseUrl, relativePath];
130131
}
131132

132-
function usesJsExtensionOnImports({ imports }: SourceFile): boolean {
133-
return firstDefined(imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || false;
133+
function usesJsExtensionOnImports({ imports }: SourceFile, preferences: ModuleSpecifierPreferences): boolean {
134+
return firstDefined(imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || !!preferences.includingJsExtensionOnAutoImports;
134135
}
135136

136137
function discoverProbableSymlinks(files: ReadonlyArray<SourceFile>, getCanonicalFileName: (file: string) => string, host: ModuleSpecifierResolutionHost) {

src/harness/fourslash.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2649,7 +2649,7 @@ Actual: ${stringify(fullActual)}`);
26492649
}
26502650
const range = ts.firstOrUndefined(ranges);
26512651

2652-
const codeFixes = this.getCodeFixes(fileName, errorCode, preferences).filter(f => f.fixId === undefined); // TODO: GH#20315 filter out those that use the import fix ID;
2652+
const codeFixes = this.getCodeFixes(fileName, errorCode, preferences).filter(f => f.fixName === "import" && f.fixId === undefined); // TODO: GH#20315 filter out those that use the import fix ID;
26532653

26542654
if (codeFixes.length === 0) {
26552655
if (expectedTextArray.length !== 0) {

src/server/protocol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2733,6 +2733,7 @@ namespace ts.server.protocol {
27332733
readonly includeCompletionsWithInsertText?: boolean;
27342734
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
27352735
readonly allowTextChangesInNewFiles?: boolean;
2736+
readonly includingJsExtensionOnAutoImports?: boolean;
27362737
}
27372738

27382739
export interface CompilerOptions {

src/services/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ namespace ts {
240240
readonly includeCompletionsWithInsertText?: boolean;
241241
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
242242
readonly allowTextChangesInNewFiles?: boolean;
243+
readonly includingJsExtensionOnAutoImports?: boolean;
243244
}
244245
/* @internal */
245246
export const defaultPreferences: UserPreferences = {};

tests/cases/fourslash/fourslash.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ declare namespace FourSlashInterface {
531531
includeCompletionsForModuleExports?: boolean;
532532
includeInsertTextCompletions?: boolean;
533533
importModuleSpecifierPreference?: "relative" | "non-relative";
534+
includingJsExtensionOnAutoImports?: boolean
534535
}
535536
interface CompletionsAtOptions extends UserPreferences {
536537
triggerCharacter?: string;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @moduleResolution: node
4+
// @noLib: true
5+
// @allowJs: true
6+
// @checkJs: true
7+
8+
// @Filename: /a.js
9+
////export function a() {}
10+
11+
// @Filename: /b.js
12+
////a;
13+
14+
// @Filename: /c.js
15+
////a;
16+
17+
// @Filename: /d.ts
18+
////a;
19+
20+
// @Filename: /e.ts
21+
////a;
22+
23+
// @Filename: /f.ts
24+
////export function f() {}
25+
26+
// @Filename: /g.ts
27+
////f;
28+
29+
// @Filename: /h.ts
30+
////export default function h() {}
31+
32+
// @Filename: /i.ts
33+
////h;
34+
35+
goTo.file("/b.js");
36+
verify.importFixAtPosition([
37+
`import { a } from "./a";
38+
39+
a;`]);
40+
41+
goTo.file("/c.js");
42+
verify.importFixAtPosition([
43+
`import { a } from "./a.js";
44+
45+
a;`], /* errorCode */ undefined, {
46+
includingJsExtensionOnAutoImports: true
47+
});
48+
49+
goTo.file("/d.ts");
50+
verify.importFixAtPosition([
51+
`import { a } from "./a";
52+
53+
a;`]);
54+
55+
goTo.file("/e.ts");
56+
verify.importFixAtPosition([
57+
`import { a } from "./a.js";
58+
59+
a;`], /* errorCode */ undefined, {
60+
includingJsExtensionOnAutoImports: true
61+
});
62+
63+
goTo.file("/g.ts");
64+
verify.importFixAtPosition([
65+
`import { f } from "./f.js";
66+
67+
f;`], /* errorCode */ undefined, {
68+
includingJsExtensionOnAutoImports: true
69+
});
70+
71+
goTo.file("/i.ts");
72+
verify.importFixAtPosition([
73+
`import h from "./h.js";
74+
75+
h;`], /* errorCode */ undefined, {
76+
includingJsExtensionOnAutoImports: true
77+
});

0 commit comments

Comments
 (0)