Partial migration of some shared vpath functionality to core

This commit is contained in:
Ron Buckton 2018-04-27 13:58:45 -07:00
parent c9c562afac
commit 8424c4d1ab
4 changed files with 96 additions and 181 deletions

View file

@ -1928,6 +1928,9 @@ namespace ts {
return text1 ? Comparison.GreaterThan : Comparison.LessThan;
}
/**
* Normalize path separators.
*/
export function normalizeSlashes(path: string): string {
return path.replace(/\\/g, "/");
}
@ -1968,27 +1971,6 @@ namespace ts {
* we expect the host to correctly handle paths in our specified format.
*/
export const directorySeparator = "/";
const directorySeparatorCharCode = CharacterCodes.slash;
function getNormalizedParts(normalizedSlashedPath: string, rootLength: number): string[] {
const parts = normalizedSlashedPath.substr(rootLength).split(directorySeparator);
const normalized: string[] = [];
for (const part of parts) {
if (part !== ".") {
if (part === ".." && normalized.length > 0 && lastOrUndefined(normalized) !== "..") {
normalized.pop();
}
else {
// A part may be an empty string (which is 'falsy') if the path had consecutive slashes,
// e.g. "path//file.ts". Drop these before re-joining the parts.
if (part) {
normalized.push(part);
}
}
}
}
return normalized;
}
export function normalizePath(path: string): string {
return normalizePathAndParts(path).path;
@ -1996,23 +1978,16 @@ namespace ts {
export function normalizePathAndParts(path: string): { path: string, parts: string[] } {
path = normalizeSlashes(path);
const rootLength = getRootLength(path);
const root = path.substr(0, rootLength);
const parts = getNormalizedParts(path, rootLength);
const [root, ...parts] = reducePathComponents(getPathComponents(path));
if (parts.length) {
const joinedParts = root + parts.join(directorySeparator);
return { path: pathEndsWithDirectorySeparator(path) ? joinedParts + directorySeparator : joinedParts, parts };
return { path: hasTrailingDirectorySeparator(path) ? joinedParts + directorySeparator : joinedParts, parts };
}
else {
return { path: root, parts };
}
}
/** A path ending with '/' refers to a directory only, never a file. */
export function pathEndsWithDirectorySeparator(path: string): boolean {
return path.charCodeAt(path.length - 1) === directorySeparatorCharCode;
}
/**
* Returns the path except for its basename. Eg:
*
@ -2085,41 +2060,88 @@ namespace ts {
return true;
}
/**
* Determines whether a path is an absolute path (e.g. starts with `/`, or a dos path
* like `c:`, `c:\` or `c:/`).
*/
export function isRootedDiskPath(path: string) {
return path && getRootLength(path) !== 0;
}
/**
* Determines whether a path consists only of a path root.
*/
export function isDiskPathRoot(path: string) {
const rootLength = getRootLength(path);
return rootLength > 0 && rootLength === path.length;
}
export function convertToRelativePath(absoluteOrRelativePath: string, basePath: string, getCanonicalFileName: (path: string) => string): string {
return !isRootedDiskPath(absoluteOrRelativePath)
? absoluteOrRelativePath
: getRelativePathToDirectoryOrUrl(basePath, absoluteOrRelativePath, basePath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
}
function normalizedPathComponents(path: string, rootLength: number) {
const normalizedParts = getNormalizedParts(path, rootLength);
return [path.substr(0, rootLength)].concat(normalizedParts);
function pathComponents(path: string, rootLength: number) {
const root = path.substring(0, rootLength);
const rest = path.substring(rootLength).split(directorySeparator);
if (rest.length && !lastOrUndefined(rest)) rest.pop();
return [root, ...rest];
}
export function getNormalizedPathComponents(path: string, currentDirectory: string) {
path = normalizeSlashes(path);
let rootLength = getRootLength(path);
if (rootLength === 0) {
// If the path is not rooted it is relative to current directory
path = combinePaths(normalizeSlashes(currentDirectory), path);
rootLength = getRootLength(path);
}
/**
* Parse a path into an array containing a root component (at index 0) and zero or more path
* components (at indices > 0). The result is not normalized.
* If the path is relative, the root component is `""`.
* If the path is absolute, the root component includes the first path separator (`/`).
*/
export function getPathComponents(path: string, currentDirectory = "") {
path = combinePaths(currentDirectory, path);
const rootLength = getRootLength(path);
return pathComponents(path, rootLength);
}
return normalizedPathComponents(path, rootLength);
export function reducePathComponents(components: ReadonlyArray<string>) {
const reduced = [components[0]];
for (let i = 1; i < components.length; i++) {
const component = components[i];
if (component === ".") continue;
if (component === "..") {
if (reduced.length > 1) {
if (reduced[reduced.length - 1] !== "..") {
reduced.pop();
continue;
}
}
else if (reduced[0]) continue;
}
reduced.push(component);
}
return reduced;
}
/**
* Parse a path into an array containing a root component (at index 0) and zero or more path
* components (at indices > 0). The result is normalized.
* If the path is relative, the root component is `""`.
* If the path is absolute, the root component includes the first path separator (`/`).
*/
export function getNormalizedPathComponents(path: string, currentDirectory: string) {
return reducePathComponents(getPathComponents(path, currentDirectory));
}
export function getNormalizedAbsolutePath(fileName: string, currentDirectory: string) {
return getNormalizedPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory));
}
/**
* Formats a parsed path consisting of a root component and zero or more path segments.
*/
export function getNormalizedPathFromPathComponents(pathComponents: ReadonlyArray<string>) {
if (pathComponents && pathComponents.length) {
return pathComponents[0] + pathComponents.slice(1).join(directorySeparator);
}
return "";
}
function getNormalizedPathComponentsOfUrl(url: string) {
@ -2153,7 +2175,7 @@ namespace ts {
// Found the "/" after the website.com so the root is length of http://www.website.com/
// and get components after the root normally like any other folder components
rootLength = indexOfNextSlash + 1;
return normalizedPathComponents(url, rootLength);
return reducePathComponents(pathComponents(url, rootLength));
}
else {
// Can't find the host assume the rest of the string as component
@ -2229,14 +2251,28 @@ namespace ts {
return i < 0 ? path : path.substring(i + 1);
}
/**
* Combines two paths. If a path is absolute, it replaces any previous path.
*/
export function combinePaths(path1: string, path2: string): string {
if (path1) path1 = normalizeSlashes(path1);
if (path2) path2 = normalizeSlashes(path2);
if (!(path1 && path1.length)) return path2;
if (!(path2 && path2.length)) return path1;
if (getRootLength(path2) !== 0) return path2;
if (path1.charAt(path1.length - 1) === directorySeparator) return path1 + path2;
if (hasTrailingDirectorySeparator(path1)) return path1 + path2;
return path1 + directorySeparator + path2;
}
/**
* Determines whether a path has a trailing separator (`/` or `\\`).
*/
export function hasTrailingDirectorySeparator(path: string) {
if (path.length === 0) return false;
const ch = path.charCodeAt(path.length - 1);
return ch === CharacterCodes.slash || ch === CharacterCodes.backslash;
}
/**
* Removes a trailing directory separator from a path.
* @param path The path.
@ -2244,7 +2280,7 @@ namespace ts {
export function removeTrailingDirectorySeparator(path: Path): Path;
export function removeTrailingDirectorySeparator(path: string): string;
export function removeTrailingDirectorySeparator(path: string) {
if (path.charAt(path.length - 1) === directorySeparator) {
if (hasTrailingDirectorySeparator(path)) {
return path.substr(0, path.length - 1);
}
@ -2258,7 +2294,7 @@ namespace ts {
export function ensureTrailingDirectorySeparator(path: Path): Path;
export function ensureTrailingDirectorySeparator(path: string): string;
export function ensureTrailingDirectorySeparator(path: string) {
if (path.charAt(path.length - 1) !== directorySeparator) {
if (!hasTrailingDirectorySeparator(path)) {
return path + directorySeparator;
}

View file

@ -806,7 +806,7 @@ namespace ts {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0_target_file_type_1, candidate, Extensions[extensions]);
}
if (!pathEndsWithDirectorySeparator(candidate)) {
if (!hasTrailingDirectorySeparator(candidate)) {
if (!onlyRecordFailures) {
const parentOfCandidate = getDirectoryPath(candidate);
if (!directoryProbablyExists(parentOfCandidate, state.host)) {

View file

@ -1,17 +1,4 @@
namespace vpath {
/**
* Virtual path separator.
*/
export import sep = ts.directorySeparator;
/**
* Normalize path separators.
*/
export import normalizeSeparators = ts.normalizeSlashes;
// export function normalizeSeparators(path: string): string {
// return ts.normalizeSlashes(path);
// }
const invalidRootComponentRegExp = /^(?!(\/|\/\/\w+\/|[a-zA-Z]:\/?|)$)/;
const invalidNavigableComponentRegExp = /[:*?"<>|]/;
const invalidNonNavigableComponentRegExp = /^\.{1,2}$|[:*?"<>|]/;
@ -93,103 +80,26 @@ namespace vpath {
return true;
}
const absolutePathRegExp = /^[\\/]([\\/](.*?[\\/](.*?[\\/])?)?)?|^[a-zA-Z]:[\\/]?|^\w+:\/{2}[^\\/]*\/?/;
import getRootLength = ts.getRootLength;
// NOTE: this differs from `ts.getRootLength` in that it doesn't support URIs.
function getRootLength(path: string) {
const match = absolutePathRegExp.exec(path);
return match ? match[0].length : 0;
}
/**
* Determines whether a path is an absolute path (e.g. starts with `/`, `\\`, or a dos path
* like `c:`).
*/
export import sep = ts.directorySeparator;
export import normalizeSeparators = ts.normalizeSlashes;
export import isAbsolute = ts.isRootedDiskPath;
// export function isAbsolute(path: string) {
// return absolutePathRegExp.test(path);
// }
/**
* Determines whether a path consists only of a path root.
*/
export function isRoot(path: string) {
const rootLength = getRootLength(path);
return rootLength > 0 && rootLength === path.length;
}
const trailingSeperatorRegExp = /[\\/]$/;
/**
* Determines whether a path has a trailing separator (`/`).
*/
export function hasTrailingSeparator(path: string) {
return trailingSeperatorRegExp.test(path) && !isRoot(path);
}
/**
* Adds a trailing separator (`/`) to a path if it doesn't have one.
*/
export import isRoot = ts.isDiskPathRoot;
export import hasTrailingSeparator = ts.hasTrailingDirectorySeparator;
export import addTrailingSeparator = ts.ensureTrailingDirectorySeparator;
// export function addTrailingSeparator(path: string) {
// return !trailingSeperatorRegExp.test(path) && path ? path + "/" : path;
// }
/**
* Removes a trailing separator (`/`) from a path if it has one.
*/
export import removeTrailingSeparator = ts.removeTrailingDirectorySeparator;
// export function removeTrailingSeparator(path: string) {
// return trailingSeperatorRegExp.test(path) && !isRoot(path) ? path.slice(0, -1) : path;
// }
function reduce(components: ReadonlyArray<string>) {
const normalized = [components[0]];
for (let i = 1; i < components.length; i++) {
const component = components[i];
if (component === ".") continue;
if (component === "..") {
if (normalized.length > 1) {
if (normalized[normalized.length - 1] !== "..") {
normalized.pop();
continue;
}
}
else if (normalized[0]) continue;
}
normalized.push(component);
}
return normalized;
}
export import normalize = ts.normalizePath;
export import combine = ts.combinePaths;
export import parse = ts.getPathComponents;
export import reduce = ts.reducePathComponents;
export import format = ts.getNormalizedPathFromPathComponents;
/**
* Normalize a path containing path traversal components (`.` or `..`).
* Combines and normalizes two paths.
*/
export function normalize(path: string): string {
const components = reduce(parse(path));
return components.length > 1 && hasTrailingSeparator(path) ? format(components) + sep : format(components);
}
/**
* Combines two or more paths. If a path is absolute, it replaces any previous path.
*/
export function combine(path: string, ...paths: string[]) {
path = normalizeSeparators(path);
for (const name of paths) {
path = ts.combinePaths(path, normalizeSeparators(name));
// name = normalizeSeparators(name);
// if (name.length === 0) continue;
// path = path.length === 0 || isAbsolute(name) ? name :
// addTrailingSeparator(path) + name;
}
return path;
}
/**
* Combines and normalizes two or more paths.
*/
export function resolve(path: string, ...paths: string[]) {
return normalize(combine(path, ...paths));
export function resolve(path1: string, path2: string) {
return normalize(combine(path1, path2));
}
// NOTE: this differs from `ts.getRelativePathToDirectoryOrUrl` in that it requires both paths
@ -322,32 +232,6 @@ namespace vpath {
return ignoreCase ? beneathCaseInsensitive(ancestor, descendant) : beneathCaseSensitive(ancestor, descendant);
}
/**
* Parse a path into a root component and zero or more path segments.
* If the path is relative, the root component is `""`.
* If the path is absolute, the root component includes the first path separator (`/`).
*/
// NOTE: this differs from `ts.getNormalizedPathComponents` due to the fact that `parse` does
// not automatically normalize relative paths and does not perform path normalization. This is
// necessary to support proper path navigation in `vfs`.
export function parse(path: string) {
path = normalizeSeparators(path);
const rootLength = getRootLength(path);
const root = path.substring(0, rootLength);
const rest = path.substring(rootLength).split(/\/+/g);
if (rest.length && !rest[rest.length - 1]) rest.pop();
return [root, ...rest.map(component => component.trim())];
}
/**
* Formats a parsed path consisting of a root component and zero or more path segments.
*/
// NOTE: this differs from `ts.getNormalizedPathFromPathComponents` in that this function
// always returns a string.
export function format(components: ReadonlyArray<string>) {
return components.length ? components[0] + components.slice(1).join(sep) : "";
}
/**
* Gets the parent directory name of a path.
*/

View file

@ -1122,11 +1122,6 @@ namespace ts {
return false;
}
export function hasTrailingDirectorySeparator(path: string) {
const lastCharacter = path.charAt(path.length - 1);
return lastCharacter === "/" || lastCharacter === "\\";
}
export function isInReferenceComment(sourceFile: SourceFile, position: number): boolean {
return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => {
const commentText = sourceFile.text.substring(c.pos, c.end);