Skip to content

Commit 8424c4d

Browse files
committed
Partial migration of some shared vpath functionality to core
1 parent c9c562a commit 8424c4d

4 files changed

Lines changed: 95 additions & 180 deletions

File tree

src/compiler/core.ts

Lines changed: 81 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,9 @@ namespace ts {
19281928
return text1 ? Comparison.GreaterThan : Comparison.LessThan;
19291929
}
19301930

1931+
/**
1932+
* Normalize path separators.
1933+
*/
19311934
export function normalizeSlashes(path: string): string {
19321935
return path.replace(/\\/g, "/");
19331936
}
@@ -1968,51 +1971,23 @@ namespace ts {
19681971
* we expect the host to correctly handle paths in our specified format.
19691972
*/
19701973
export const directorySeparator = "/";
1971-
const directorySeparatorCharCode = CharacterCodes.slash;
1972-
function getNormalizedParts(normalizedSlashedPath: string, rootLength: number): string[] {
1973-
const parts = normalizedSlashedPath.substr(rootLength).split(directorySeparator);
1974-
const normalized: string[] = [];
1975-
for (const part of parts) {
1976-
if (part !== ".") {
1977-
if (part === ".." && normalized.length > 0 && lastOrUndefined(normalized) !== "..") {
1978-
normalized.pop();
1979-
}
1980-
else {
1981-
// A part may be an empty string (which is 'falsy') if the path had consecutive slashes,
1982-
// e.g. "path//file.ts". Drop these before re-joining the parts.
1983-
if (part) {
1984-
normalized.push(part);
1985-
}
1986-
}
1987-
}
1988-
}
1989-
1990-
return normalized;
1991-
}
19921974

19931975
export function normalizePath(path: string): string {
19941976
return normalizePathAndParts(path).path;
19951977
}
19961978

19971979
export function normalizePathAndParts(path: string): { path: string, parts: string[] } {
19981980
path = normalizeSlashes(path);
1999-
const rootLength = getRootLength(path);
2000-
const root = path.substr(0, rootLength);
2001-
const parts = getNormalizedParts(path, rootLength);
1981+
const [root, ...parts] = reducePathComponents(getPathComponents(path));
20021982
if (parts.length) {
20031983
const joinedParts = root + parts.join(directorySeparator);
2004-
return { path: pathEndsWithDirectorySeparator(path) ? joinedParts + directorySeparator : joinedParts, parts };
1984+
return { path: hasTrailingDirectorySeparator(path) ? joinedParts + directorySeparator : joinedParts, parts };
20051985
}
20061986
else {
20071987
return { path: root, parts };
20081988
}
20091989
}
20101990

2011-
/** A path ending with '/' refers to a directory only, never a file. */
2012-
export function pathEndsWithDirectorySeparator(path: string): boolean {
2013-
return path.charCodeAt(path.length - 1) === directorySeparatorCharCode;
2014-
}
2015-
20161991
/**
20171992
* Returns the path except for its basename. Eg:
20181993
*
@@ -2085,41 +2060,88 @@ namespace ts {
20852060
return true;
20862061
}
20872062

2063+
/**
2064+
* Determines whether a path is an absolute path (e.g. starts with `/`, or a dos path
2065+
* like `c:`, `c:\` or `c:/`).
2066+
*/
20882067
export function isRootedDiskPath(path: string) {
20892068
return path && getRootLength(path) !== 0;
20902069
}
20912070

2071+
/**
2072+
* Determines whether a path consists only of a path root.
2073+
*/
2074+
export function isDiskPathRoot(path: string) {
2075+
const rootLength = getRootLength(path);
2076+
return rootLength > 0 && rootLength === path.length;
2077+
}
2078+
20922079
export function convertToRelativePath(absoluteOrRelativePath: string, basePath: string, getCanonicalFileName: (path: string) => string): string {
20932080
return !isRootedDiskPath(absoluteOrRelativePath)
20942081
? absoluteOrRelativePath
20952082
: getRelativePathToDirectoryOrUrl(basePath, absoluteOrRelativePath, basePath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
20962083
}
20972084

2098-
function normalizedPathComponents(path: string, rootLength: number) {
2099-
const normalizedParts = getNormalizedParts(path, rootLength);
2100-
return [path.substr(0, rootLength)].concat(normalizedParts);
2085+
function pathComponents(path: string, rootLength: number) {
2086+
const root = path.substring(0, rootLength);
2087+
const rest = path.substring(rootLength).split(directorySeparator);
2088+
if (rest.length && !lastOrUndefined(rest)) rest.pop();
2089+
return [root, ...rest];
21012090
}
21022091

2103-
export function getNormalizedPathComponents(path: string, currentDirectory: string) {
2104-
path = normalizeSlashes(path);
2105-
let rootLength = getRootLength(path);
2106-
if (rootLength === 0) {
2107-
// If the path is not rooted it is relative to current directory
2108-
path = combinePaths(normalizeSlashes(currentDirectory), path);
2109-
rootLength = getRootLength(path);
2092+
/**
2093+
* Parse a path into an array containing a root component (at index 0) and zero or more path
2094+
* components (at indices > 0). The result is not normalized.
2095+
* If the path is relative, the root component is `""`.
2096+
* If the path is absolute, the root component includes the first path separator (`/`).
2097+
*/
2098+
export function getPathComponents(path: string, currentDirectory = "") {
2099+
path = combinePaths(currentDirectory, path);
2100+
const rootLength = getRootLength(path);
2101+
return pathComponents(path, rootLength);
2102+
}
2103+
2104+
export function reducePathComponents(components: ReadonlyArray<string>) {
2105+
const reduced = [components[0]];
2106+
for (let i = 1; i < components.length; i++) {
2107+
const component = components[i];
2108+
if (component === ".") continue;
2109+
if (component === "..") {
2110+
if (reduced.length > 1) {
2111+
if (reduced[reduced.length - 1] !== "..") {
2112+
reduced.pop();
2113+
continue;
2114+
}
2115+
}
2116+
else if (reduced[0]) continue;
2117+
}
2118+
reduced.push(component);
21102119
}
2120+
return reduced;
2121+
}
21112122

2112-
return normalizedPathComponents(path, rootLength);
2123+
/**
2124+
* Parse a path into an array containing a root component (at index 0) and zero or more path
2125+
* components (at indices > 0). The result is normalized.
2126+
* If the path is relative, the root component is `""`.
2127+
* If the path is absolute, the root component includes the first path separator (`/`).
2128+
*/
2129+
export function getNormalizedPathComponents(path: string, currentDirectory: string) {
2130+
return reducePathComponents(getPathComponents(path, currentDirectory));
21132131
}
21142132

21152133
export function getNormalizedAbsolutePath(fileName: string, currentDirectory: string) {
21162134
return getNormalizedPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory));
21172135
}
21182136

2137+
/**
2138+
* Formats a parsed path consisting of a root component and zero or more path segments.
2139+
*/
21192140
export function getNormalizedPathFromPathComponents(pathComponents: ReadonlyArray<string>) {
21202141
if (pathComponents && pathComponents.length) {
21212142
return pathComponents[0] + pathComponents.slice(1).join(directorySeparator);
21222143
}
2144+
return "";
21232145
}
21242146

21252147
function getNormalizedPathComponentsOfUrl(url: string) {
@@ -2153,7 +2175,7 @@ namespace ts {
21532175
// Found the "/" after the website.com so the root is length of http://www.website.com/
21542176
// and get components after the root normally like any other folder components
21552177
rootLength = indexOfNextSlash + 1;
2156-
return normalizedPathComponents(url, rootLength);
2178+
return reducePathComponents(pathComponents(url, rootLength));
21572179
}
21582180
else {
21592181
// Can't find the host assume the rest of the string as component
@@ -2229,22 +2251,36 @@ namespace ts {
22292251
return i < 0 ? path : path.substring(i + 1);
22302252
}
22312253

2254+
/**
2255+
* Combines two paths. If a path is absolute, it replaces any previous path.
2256+
*/
22322257
export function combinePaths(path1: string, path2: string): string {
2258+
if (path1) path1 = normalizeSlashes(path1);
2259+
if (path2) path2 = normalizeSlashes(path2);
22332260
if (!(path1 && path1.length)) return path2;
22342261
if (!(path2 && path2.length)) return path1;
22352262
if (getRootLength(path2) !== 0) return path2;
2236-
if (path1.charAt(path1.length - 1) === directorySeparator) return path1 + path2;
2263+
if (hasTrailingDirectorySeparator(path1)) return path1 + path2;
22372264
return path1 + directorySeparator + path2;
22382265
}
22392266

2267+
/**
2268+
* Determines whether a path has a trailing separator (`/` or `\\`).
2269+
*/
2270+
export function hasTrailingDirectorySeparator(path: string) {
2271+
if (path.length === 0) return false;
2272+
const ch = path.charCodeAt(path.length - 1);
2273+
return ch === CharacterCodes.slash || ch === CharacterCodes.backslash;
2274+
}
2275+
22402276
/**
22412277
* Removes a trailing directory separator from a path.
22422278
* @param path The path.
22432279
*/
22442280
export function removeTrailingDirectorySeparator(path: Path): Path;
22452281
export function removeTrailingDirectorySeparator(path: string): string;
22462282
export function removeTrailingDirectorySeparator(path: string) {
2247-
if (path.charAt(path.length - 1) === directorySeparator) {
2283+
if (hasTrailingDirectorySeparator(path)) {
22482284
return path.substr(0, path.length - 1);
22492285
}
22502286

@@ -2258,7 +2294,7 @@ namespace ts {
22582294
export function ensureTrailingDirectorySeparator(path: Path): Path;
22592295
export function ensureTrailingDirectorySeparator(path: string): string;
22602296
export function ensureTrailingDirectorySeparator(path: string) {
2261-
if (path.charAt(path.length - 1) !== directorySeparator) {
2297+
if (!hasTrailingDirectorySeparator(path)) {
22622298
return path + directorySeparator;
22632299
}
22642300

src/compiler/moduleNameResolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ namespace ts {
806806
if (state.traceEnabled) {
807807
trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0_target_file_type_1, candidate, Extensions[extensions]);
808808
}
809-
if (!pathEndsWithDirectorySeparator(candidate)) {
809+
if (!hasTrailingDirectorySeparator(candidate)) {
810810
if (!onlyRecordFailures) {
811811
const parentOfCandidate = getDirectoryPath(candidate);
812812
if (!directoryProbablyExists(parentOfCandidate, state.host)) {

src/harness/vpath.ts

Lines changed: 13 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,4 @@
11
namespace vpath {
2-
/**
3-
* Virtual path separator.
4-
*/
5-
export import sep = ts.directorySeparator;
6-
7-
/**
8-
* Normalize path separators.
9-
*/
10-
export import normalizeSeparators = ts.normalizeSlashes;
11-
// export function normalizeSeparators(path: string): string {
12-
// return ts.normalizeSlashes(path);
13-
// }
14-
152
const invalidRootComponentRegExp = /^(?!(\/|\/\/\w+\/|[a-zA-Z]:\/?|)$)/;
163
const invalidNavigableComponentRegExp = /[:*?"<>|]/;
174
const invalidNonNavigableComponentRegExp = /^\.{1,2}$|[:*?"<>|]/;
@@ -93,103 +80,26 @@ namespace vpath {
9380
return true;
9481
}
9582

96-
const absolutePathRegExp = /^[\\/]([\\/](.*?[\\/](.*?[\\/])?)?)?|^[a-zA-Z]:[\\/]?|^\w+:\/{2}[^\\/]*\/?/;
97-
98-
// NOTE: this differs from `ts.getRootLength` in that it doesn't support URIs.
99-
function getRootLength(path: string) {
100-
const match = absolutePathRegExp.exec(path);
101-
return match ? match[0].length : 0;
102-
}
83+
import getRootLength = ts.getRootLength;
10384

104-
/**
105-
* Determines whether a path is an absolute path (e.g. starts with `/`, `\\`, or a dos path
106-
* like `c:`).
107-
*/
85+
export import sep = ts.directorySeparator;
86+
export import normalizeSeparators = ts.normalizeSlashes;
10887
export import isAbsolute = ts.isRootedDiskPath;
109-
// export function isAbsolute(path: string) {
110-
// return absolutePathRegExp.test(path);
111-
// }
112-
113-
/**
114-
* Determines whether a path consists only of a path root.
115-
*/
116-
export function isRoot(path: string) {
117-
const rootLength = getRootLength(path);
118-
return rootLength > 0 && rootLength === path.length;
119-
}
120-
121-
const trailingSeperatorRegExp = /[\\/]$/;
122-
123-
/**
124-
* Determines whether a path has a trailing separator (`/`).
125-
*/
126-
export function hasTrailingSeparator(path: string) {
127-
return trailingSeperatorRegExp.test(path) && !isRoot(path);
128-
}
129-
130-
/**
131-
* Adds a trailing separator (`/`) to a path if it doesn't have one.
132-
*/
88+
export import isRoot = ts.isDiskPathRoot;
89+
export import hasTrailingSeparator = ts.hasTrailingDirectorySeparator;
13390
export import addTrailingSeparator = ts.ensureTrailingDirectorySeparator;
134-
// export function addTrailingSeparator(path: string) {
135-
// return !trailingSeperatorRegExp.test(path) && path ? path + "/" : path;
136-
// }
137-
138-
/**
139-
* Removes a trailing separator (`/`) from a path if it has one.
140-
*/
14191
export import removeTrailingSeparator = ts.removeTrailingDirectorySeparator;
142-
// export function removeTrailingSeparator(path: string) {
143-
// return trailingSeperatorRegExp.test(path) && !isRoot(path) ? path.slice(0, -1) : path;
144-
// }
145-
146-
function reduce(components: ReadonlyArray<string>) {
147-
const normalized = [components[0]];
148-
for (let i = 1; i < components.length; i++) {
149-
const component = components[i];
150-
if (component === ".") continue;
151-
if (component === "..") {
152-
if (normalized.length > 1) {
153-
if (normalized[normalized.length - 1] !== "..") {
154-
normalized.pop();
155-
continue;
156-
}
157-
}
158-
else if (normalized[0]) continue;
159-
}
160-
normalized.push(component);
161-
}
162-
return normalized;
163-
}
92+
export import normalize = ts.normalizePath;
93+
export import combine = ts.combinePaths;
94+
export import parse = ts.getPathComponents;
95+
export import reduce = ts.reducePathComponents;
96+
export import format = ts.getNormalizedPathFromPathComponents;
16497

16598
/**
166-
* Normalize a path containing path traversal components (`.` or `..`).
99+
* Combines and normalizes two paths.
167100
*/
168-
export function normalize(path: string): string {
169-
const components = reduce(parse(path));
170-
return components.length > 1 && hasTrailingSeparator(path) ? format(components) + sep : format(components);
171-
}
172-
173-
/**
174-
* Combines two or more paths. If a path is absolute, it replaces any previous path.
175-
*/
176-
export function combine(path: string, ...paths: string[]) {
177-
path = normalizeSeparators(path);
178-
for (const name of paths) {
179-
path = ts.combinePaths(path, normalizeSeparators(name));
180-
// name = normalizeSeparators(name);
181-
// if (name.length === 0) continue;
182-
// path = path.length === 0 || isAbsolute(name) ? name :
183-
// addTrailingSeparator(path) + name;
184-
}
185-
return path;
186-
}
187-
188-
/**
189-
* Combines and normalizes two or more paths.
190-
*/
191-
export function resolve(path: string, ...paths: string[]) {
192-
return normalize(combine(path, ...paths));
101+
export function resolve(path1: string, path2: string) {
102+
return normalize(combine(path1, path2));
193103
}
194104

195105
// NOTE: this differs from `ts.getRelativePathToDirectoryOrUrl` in that it requires both paths
@@ -322,32 +232,6 @@ namespace vpath {
322232
return ignoreCase ? beneathCaseInsensitive(ancestor, descendant) : beneathCaseSensitive(ancestor, descendant);
323233
}
324234

325-
/**
326-
* Parse a path into a root component and zero or more path segments.
327-
* If the path is relative, the root component is `""`.
328-
* If the path is absolute, the root component includes the first path separator (`/`).
329-
*/
330-
// NOTE: this differs from `ts.getNormalizedPathComponents` due to the fact that `parse` does
331-
// not automatically normalize relative paths and does not perform path normalization. This is
332-
// necessary to support proper path navigation in `vfs`.
333-
export function parse(path: string) {
334-
path = normalizeSeparators(path);
335-
const rootLength = getRootLength(path);
336-
const root = path.substring(0, rootLength);
337-
const rest = path.substring(rootLength).split(/\/+/g);
338-
if (rest.length && !rest[rest.length - 1]) rest.pop();
339-
return [root, ...rest.map(component => component.trim())];
340-
}
341-
342-
/**
343-
* Formats a parsed path consisting of a root component and zero or more path segments.
344-
*/
345-
// NOTE: this differs from `ts.getNormalizedPathFromPathComponents` in that this function
346-
// always returns a string.
347-
export function format(components: ReadonlyArray<string>) {
348-
return components.length ? components[0] + components.slice(1).join(sep) : "";
349-
}
350-
351235
/**
352236
* Gets the parent directory name of a path.
353237
*/

0 commit comments

Comments
 (0)