Skip to content

Commit 753feb8

Browse files
Jesse Trinityandrewbranch
Jesse Trinity
andauthored
importModuleSpecifierEnding changes .ts string completions to .js (#44602)
* don't add .ts extensions to imports * Update src/services/stringCompletions.ts Co-authored-by: Andrew Branch <[email protected]> * add other supported extension types * add final newlines * adress PR comment * add unsupported extension test Co-authored-by: Andrew Branch <[email protected]>
1 parent 0a9b218 commit 753feb8

9 files changed

+95
-15
lines changed

src/compiler/moduleSpecifiers.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,11 @@ namespace ts.moduleSpecifiers {
694694
}
695695

696696
function getJSExtensionForFile(fileName: string, options: CompilerOptions): Extension {
697-
const ext = extensionFromPath(fileName);
697+
return tryGetJSExtensionForFile(fileName, options) ?? Debug.fail(`Extension ${extensionFromPath(fileName)} is unsupported:: FileName:: ${fileName}`);
698+
}
699+
700+
export function tryGetJSExtensionForFile(fileName: string, options: CompilerOptions): Extension | undefined {
701+
const ext = tryGetExtensionFromPath(fileName);
698702
switch (ext) {
699703
case Extension.Ts:
700704
case Extension.Dts:
@@ -705,10 +709,8 @@ namespace ts.moduleSpecifiers {
705709
case Extension.Jsx:
706710
case Extension.Json:
707711
return ext;
708-
case Extension.TsBuildInfo:
709-
return Debug.fail(`Extension ${Extension.TsBuildInfo} is unsupported:: FileName:: ${fileName}`);
710712
default:
711-
return Debug.assertNever(ext);
713+
return undefined;
712714
}
713715
}
714716

src/services/stringCompletions.ts

+27-9
Original file line numberDiff line numberDiff line change
@@ -321,13 +321,14 @@ namespace ts.Completions.StringCompletions {
321321

322322
interface ExtensionOptions {
323323
readonly extensions: readonly Extension[];
324-
readonly includeExtensions: boolean;
324+
readonly includeExtensionsOption: IncludeExtensionsOption;
325325
}
326-
function getExtensionOptions(compilerOptions: CompilerOptions, includeExtensions = false): ExtensionOptions {
327-
return { extensions: getSupportedExtensionsForModuleResolution(compilerOptions), includeExtensions };
326+
function getExtensionOptions(compilerOptions: CompilerOptions, includeExtensionsOption = IncludeExtensionsOption.Exclude): ExtensionOptions {
327+
return { extensions: getSupportedExtensionsForModuleResolution(compilerOptions), includeExtensionsOption };
328328
}
329329
function getCompletionEntriesForRelativeModules(literalValue: string, scriptDirectory: string, compilerOptions: CompilerOptions, host: LanguageServiceHost, scriptPath: Path, preferences: UserPreferences) {
330-
const extensionOptions = getExtensionOptions(compilerOptions, preferences.importModuleSpecifierEnding === "js");
330+
const includeExtensions = preferences.importModuleSpecifierEnding === "js" ? IncludeExtensionsOption.ModuleSpecifierCompletion : IncludeExtensionsOption.Exclude;
331+
const extensionOptions = getExtensionOptions(compilerOptions, includeExtensions);
331332
if (compilerOptions.rootDirs) {
332333
return getCompletionEntriesForDirectoryFragmentWithRootDirs(
333334
compilerOptions.rootDirs, literalValue, scriptDirectory, extensionOptions, compilerOptions, host, scriptPath);
@@ -370,10 +371,15 @@ namespace ts.Completions.StringCompletions {
370371
return flatMap(baseDirectories, baseDirectory => getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensionOptions, host, exclude));
371372
}
372373

374+
const enum IncludeExtensionsOption {
375+
Exclude,
376+
Include,
377+
ModuleSpecifierCompletion,
378+
}
373379
/**
374380
* Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename.
375381
*/
376-
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, { extensions, includeExtensions }: ExtensionOptions, host: LanguageServiceHost, exclude?: string, result: NameAndKind[] = []): NameAndKind[] {
382+
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, { extensions, includeExtensionsOption }: ExtensionOptions, host: LanguageServiceHost, exclude?: string, result: NameAndKind[] = []): NameAndKind[] {
377383
if (fragment === undefined) {
378384
fragment = "";
379385
}
@@ -407,7 +413,7 @@ namespace ts.Completions.StringCompletions {
407413
if (files) {
408414
/**
409415
* Multiple file entries might map to the same truncated name once we remove extensions
410-
* (happens iff includeExtensions === false)so we use a set-like data structure. Eg:
416+
* (happens iff includeExtensionsOption === includeExtensionsOption.Exclude) so we use a set-like data structure. Eg:
411417
*
412418
* both foo.ts and foo.tsx become foo
413419
*/
@@ -418,8 +424,20 @@ namespace ts.Completions.StringCompletions {
418424
continue;
419425
}
420426

421-
const foundFileName = includeExtensions || fileExtensionIs(filePath, Extension.Json) ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath));
422-
foundFiles.set(foundFileName, tryGetExtensionFromPath(filePath));
427+
let foundFileName: string;
428+
const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(filePath, host.getCompilationSettings());
429+
if (includeExtensionsOption === IncludeExtensionsOption.Exclude && !fileExtensionIs(filePath, Extension.Json)) {
430+
foundFileName = removeFileExtension(getBaseFileName(filePath));
431+
foundFiles.set(foundFileName, tryGetExtensionFromPath(filePath));
432+
}
433+
else if (includeExtensionsOption === IncludeExtensionsOption.ModuleSpecifierCompletion && outputExtension) {
434+
foundFileName = changeExtension(getBaseFileName(filePath), outputExtension);
435+
foundFiles.set(foundFileName, outputExtension);
436+
}
437+
else {
438+
foundFileName = getBaseFileName(filePath);
439+
foundFiles.set(foundFileName, tryGetExtensionFromPath(filePath));
440+
}
423441
}
424442

425443
foundFiles.forEach((ext, foundFile) => {
@@ -635,7 +653,7 @@ namespace ts.Completions.StringCompletions {
635653

636654
const [, prefix, kind, toComplete] = match;
637655
const scriptPath = getDirectoryPath(sourceFile.path);
638-
const names = kind === "path" ? getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getExtensionOptions(compilerOptions, /*includeExtensions*/ true), host, sourceFile.path)
656+
const names = kind === "path" ? getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getExtensionOptions(compilerOptions, IncludeExtensionsOption.Include), host, sourceFile.path)
639657
: kind === "types" ? getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, getFragmentDirectory(toComplete), getExtensionOptions(compilerOptions))
640658
: Debug.fail();
641659
return addReplacementSpans(toComplete, range.pos + prefix.length, names);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference path='fourslash.ts'/>
2+
//@Filename:test.d.ts
3+
//// export declare class Test {}
4+
5+
//@Filename:module.ts
6+
////import { Test } from ".//**/"
7+
8+
verify.completions({ marker: "", includes:{name:"test.js"}, preferences: {importModuleSpecifierEnding: "js" }, isNewIdentifierLocation: true});
9+
verify.completions({ marker: "", includes:{name:"test"}, preferences: {importModuleSpecifierEnding: "index" }, isNewIdentifierLocation: true});

tests/cases/fourslash/completionImportModuleSpecifierEnding.ts renamed to tests/cases/fourslash/completionImportModuleSpecifierEndingJs.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
//@Filename:module.js
99
////import { f } from ".//**/"
1010

11-
12-
verify.completions({ marker: "", includes:{name:"test.js"}, preferences: {importModuleSpecifierEnding: "js" }, isNewIdentifierLocation: true})
11+
verify.completions({ marker: "", includes:{name:"test.js"}, preferences: {importModuleSpecifierEnding: "js" }, isNewIdentifierLocation: true});
12+
verify.completions({ marker: "", includes:{name:"test"}, preferences: {importModuleSpecifierEnding: "index" }, isNewIdentifierLocation: true});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path='fourslash.ts'/>
2+
//@allowJs: true
3+
//@jsx:preserve
4+
//@Filename:test.jsx
5+
//// export class Test { }
6+
7+
//@Filename:module.jsx
8+
////import { Test } from ".//**/"
9+
10+
verify.completions({ marker: "", includes:{name:"test.jsx"}, preferences: {importModuleSpecifierEnding: "js"}, isNewIdentifierLocation: true});
11+
verify.completions({ marker: "", includes:{name:"test"}, preferences: {importModuleSpecifierEnding: "index" }, isNewIdentifierLocation: true});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path='fourslash.ts'/>
2+
//@Filename:test.ts
3+
////export function f(){
4+
//// return 1
5+
////}
6+
7+
//@Filename:module.ts
8+
////import { f } from ".//**/"
9+
10+
verify.completions({ marker: "", includes:{name:"test.js"}, preferences: {importModuleSpecifierEnding: "js" }, isNewIdentifierLocation: true});
11+
verify.completions({ marker: "", includes:{name:"test"}, preferences: {importModuleSpecifierEnding: "index" }, isNewIdentifierLocation: true});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/// <reference path='fourslash.ts'/>
2+
//@jsx:preserve
3+
//@Filename:test.tsx
4+
//// export class Test { }
5+
6+
//@Filename:module.tsx
7+
////import { Test } from ".//**/"
8+
9+
verify.completions({ marker: "", includes:{name:"test.jsx"}, preferences: {importModuleSpecifierEnding: "js"}, isNewIdentifierLocation: true});
10+
verify.completions({ marker: "", includes:{name:"test"}, preferences: {importModuleSpecifierEnding: "index" }, isNewIdentifierLocation: true});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/// <reference path='fourslash.ts'/>
2+
//@jsx:react
3+
//@Filename:test.tsx
4+
//// export class Test { }
5+
6+
//@Filename:module.tsx
7+
////import { Test } from ".//**/"
8+
9+
verify.completions({ marker: "", includes:{name:"test.js"}, preferences: {importModuleSpecifierEnding: "js"}, isNewIdentifierLocation: true});
10+
verify.completions({ marker: "", includes:{name:"test"}, preferences: {importModuleSpecifierEnding: "index" }, isNewIdentifierLocation: true});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference path='fourslash.ts'/>
2+
//@Filename:index.css
3+
//// body {}
4+
5+
//@Filename:module.ts
6+
////import ".//**/"
7+
8+
verify.completions({ marker: "", excludes:"index.css", preferences: {importModuleSpecifierEnding: "js" }, isNewIdentifierLocation: true});
9+
verify.completions({ marker: "", excludes:"index", preferences: {importModuleSpecifierEnding: "index" }, isNewIdentifierLocation: true});

0 commit comments

Comments
 (0)