Skip to content

Commit 5ce32cc

Browse files
committed
factor out helpers + cleanup startsWith and friends
1 parent f2e5fa5 commit 5ce32cc

File tree

4 files changed

+31
-22
lines changed

4 files changed

+31
-22
lines changed

src/compiler/core.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,13 +1730,18 @@ namespace ts {
17301730

17311731
/* @internal */
17321732
export function startsWith(str: string, prefix: string): boolean {
1733-
return str.lastIndexOf(prefix, 0) === 0;
1733+
return str.indexOf(prefix) === 0;
1734+
}
1735+
1736+
/* @internal */
1737+
export function removePrefix(str: string, prefix: string | undefined): string {
1738+
return startsWith(str, prefix) ? str.substr(prefix.length) : str;
17341739
}
17351740

17361741
/* @internal */
17371742
export function endsWith(str: string, suffix: string): boolean {
17381743
const expectedPos = str.length - suffix.length;
1739-
return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos;
1744+
return expectedPos >= 0 && str.lastIndexOf(suffix, expectedPos) === expectedPos;
17401745
}
17411746

17421747
export function hasExtension(fileName: string): boolean {
@@ -1747,7 +1752,7 @@ namespace ts {
17471752
return path.length > extension.length && endsWith(path, extension);
17481753
}
17491754

1750-
export function fileExtensionIsAny(path: string, extensions: string[]): boolean {
1755+
export function fileExtensionIsOneOf(path: string, extensions: string[]): boolean {
17511756
for (const extension of extensions) {
17521757
if (fileExtensionIs(path, extension)) {
17531758
return true;
@@ -1947,7 +1952,7 @@ namespace ts {
19471952
for (const current of files) {
19481953
const name = combinePaths(path, current);
19491954
const absoluteName = combinePaths(absolutePath, current);
1950-
if (extensions && !fileExtensionIsAny(name, extensions)) continue;
1955+
if (extensions && !fileExtensionIsOneOf(name, extensions)) continue;
19511956
if (excludeRegex && excludeRegex.test(absoluteName)) continue;
19521957
if (!includeFileRegexes) {
19531958
results[0].push(name);

src/compiler/moduleNameResolver.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -958,10 +958,13 @@ namespace ts {
958958
}
959959
}
960960

961+
/** Double underscores are used in DefinitelyTyped to delimit scoped packages. */
962+
const mangledScopedPackageSeparator = "__";
963+
961964
/** For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`. */
962965
function mangleScopedPackage(moduleName: string, state: ModuleResolutionState): string {
963966
if (startsWith(moduleName, "@")) {
964-
const replaceSlash = moduleName.replace(ts.directorySeparator, "__");
967+
const replaceSlash = moduleName.replace(ts.directorySeparator, mangledScopedPackageSeparator);
965968
if (replaceSlash !== moduleName) {
966969
const mangled = replaceSlash.slice(1); // Take off the "@"
967970
if (state.traceEnabled) {
@@ -973,6 +976,16 @@ namespace ts {
973976
return moduleName;
974977
}
975978

979+
export function getPackageNameFromAtTypesDirectory(mangledName: string): string {
980+
const withoutAtTypePrefix = removePrefix(mangledName, "@types/");
981+
if (withoutAtTypePrefix !== mangledName) {
982+
return withoutAtTypePrefix.indexOf("__") !== -1 ?
983+
"@" + withoutAtTypePrefix.replace(mangledScopedPackageSeparator, ts.directorySeparator) :
984+
withoutAtTypePrefix;
985+
}
986+
return mangledName;
987+
}
988+
976989
function tryFindNonRelativeModuleNameInCache(cache: PerModuleNameCache | undefined, moduleName: string, containingDirectory: string, traceEnabled: boolean, host: ModuleResolutionHost): SearchResult<Resolved> {
977990
const result = cache && cache.get(containingDirectory);
978991
if (result) {

src/services/codefixes/importFixes.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -501,14 +501,6 @@ namespace ts.codefix {
501501
relativeFileName = getRelativePath(moduleFileName, sourceDirectory);
502502
}
503503

504-
if (startsWith(relativeFileName, "@types/")) {
505-
relativeFileName = relativeFileName.substr(/*"@types".length*/ 7);
506-
if (relativeFileName.indexOf("__") !== -1) {
507-
// Double underscores are used in DefinitelyTyped to delimit scoped packages.
508-
relativeFileName = "@" + relativeFileName.replace("__", "/");
509-
}
510-
}
511-
512504
relativeFileName = removeFileExtension(relativeFileName);
513505
if (endsWith(relativeFileName, "/index")) {
514506
relativeFileName = getDirectoryPath(relativeFileName);
@@ -530,6 +522,8 @@ namespace ts.codefix {
530522
catch (e) { }
531523
}
532524

525+
relativeFileName = getPackageNameFromAtTypesDirectory(relativeFileName);
526+
533527
return relativeFileName;
534528
}
535529
}

src/services/pathCompletions.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -245,41 +245,38 @@ namespace ts.Completions.PathCompletions {
245245

246246
// Get modules that the type checker picked up
247247
const ambientModules = map(typeChecker.getAmbientModules(), sym => stripQuotes(sym.name));
248-
let nonRelativeModules = filter(ambientModules, moduleName => startsWith(moduleName, fragment));
248+
let nonRelativeModuleNames = filter(ambientModules, moduleName => startsWith(moduleName, fragment));
249249

250250
// Nested modules of the form "module-name/sub" need to be adjusted to only return the string
251251
// after the last '/' that appears in the fragment because that's where the replacement span
252252
// starts
253253
if (isNestedModule) {
254254
const moduleNameWithSeperator = ensureTrailingDirectorySeparator(moduleNameFragment);
255-
nonRelativeModules = map(nonRelativeModules, moduleName => {
256-
if (startsWith(fragment, moduleNameWithSeperator)) {
257-
return moduleName.substr(moduleNameWithSeperator.length);
258-
}
259-
return moduleName;
255+
nonRelativeModuleNames = map(nonRelativeModuleNames, nonRelativeModuleName => {
256+
return removePrefix(nonRelativeModuleName, moduleNameWithSeperator);
260257
});
261258
}
262259

263260

264261
if (!options.moduleResolution || options.moduleResolution === ModuleResolutionKind.NodeJs) {
265262
for (const visibleModule of enumerateNodeModulesVisibleToScript(host, scriptPath)) {
266263
if (!isNestedModule) {
267-
nonRelativeModules.push(visibleModule.moduleName);
264+
nonRelativeModuleNames.push(visibleModule.moduleName);
268265
}
269266
else if (startsWith(visibleModule.moduleName, moduleNameFragment)) {
270267
const nestedFiles = tryReadDirectory(host, visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/ undefined, /*include*/ ["./*"]);
271268
if (nestedFiles) {
272269
for (let f of nestedFiles) {
273270
f = normalizePath(f);
274271
const nestedModule = removeFileExtension(getBaseFileName(f));
275-
nonRelativeModules.push(nestedModule);
272+
nonRelativeModuleNames.push(nestedModule);
276273
}
277274
}
278275
}
279276
}
280277
}
281278

282-
return deduplicate(nonRelativeModules);
279+
return deduplicate(nonRelativeModuleNames);
283280
}
284281

285282
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): CompletionInfo {

0 commit comments

Comments
 (0)