mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-14 19:16:17 -06:00
More path cleanup
This commit is contained in:
parent
4863d55d01
commit
1dff0af646
@ -1819,7 +1819,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getDefaultCompilerOptions(configFileName?: string) {
|
||||
const options: CompilerOptions = getBaseFileName(configFileName) === "jsconfig.json"
|
||||
const options: CompilerOptions = configFileName && getBaseFileName(configFileName) === "jsconfig.json"
|
||||
? { allowJs: true, maxNodeModuleJsDepth: 2, allowSyntheticDefaultImports: true, skipLibCheck: true, noEmit: true }
|
||||
: {};
|
||||
return options;
|
||||
@ -1834,7 +1834,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getDefaultTypeAcquisition(configFileName?: string): TypeAcquisition {
|
||||
return { enable: getBaseFileName(configFileName) === "jsconfig.json", include: [], exclude: [] };
|
||||
return { enable: configFileName && getBaseFileName(configFileName) === "jsconfig.json", include: [], exclude: [] };
|
||||
}
|
||||
|
||||
function convertTypeAcquisitionFromJsonWorker(jsonOptions: any,
|
||||
|
||||
@ -1928,85 +1928,6 @@ namespace ts {
|
||||
return text1 ? Comparison.GreaterThan : Comparison.LessThan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize path separators.
|
||||
*/
|
||||
export function normalizeSlashes(path: string): string {
|
||||
return path.replace(/\\/g, "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns length of path root (i.e. length of "/", "x:/", "//server/share/, file:///user/files")
|
||||
*/
|
||||
export function getRootLength(path: string): number {
|
||||
if (path.charCodeAt(0) === CharacterCodes.slash) {
|
||||
if (path.charCodeAt(1) !== CharacterCodes.slash) return 1;
|
||||
const p1 = path.indexOf("/", 2);
|
||||
if (p1 < 0) return 2;
|
||||
const p2 = path.indexOf("/", p1 + 1);
|
||||
if (p2 < 0) return p1 + 1;
|
||||
return p2 + 1;
|
||||
}
|
||||
if (path.charCodeAt(1) === CharacterCodes.colon) {
|
||||
if (path.charCodeAt(2) === CharacterCodes.slash || path.charCodeAt(2) === CharacterCodes.backslash) return 3;
|
||||
}
|
||||
// Per RFC 1738 'file' URI schema has the shape file://<host>/<path>
|
||||
// if <host> is omitted then it is assumed that host value is 'localhost',
|
||||
// however slash after the omitted <host> is not removed.
|
||||
// file:///folder1/file1 - this is a correct URI
|
||||
// file://folder2/file2 - this is an incorrect URI
|
||||
if (path.lastIndexOf("file:///", 0) === 0) {
|
||||
return "file:///".length;
|
||||
}
|
||||
const idx = path.indexOf("://");
|
||||
if (idx !== -1) {
|
||||
return idx + "://".length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internally, we represent paths as strings with '/' as the directory separator.
|
||||
* When we make system calls (eg: LanguageServiceHost.getDirectory()),
|
||||
* we expect the host to correctly handle paths in our specified format.
|
||||
*/
|
||||
export const directorySeparator = "/";
|
||||
|
||||
export function normalizePath(path: string): string {
|
||||
return normalizePathAndParts(path).path;
|
||||
}
|
||||
|
||||
export function normalizePathAndParts(path: string): { path: string, parts: string[] } {
|
||||
path = normalizeSlashes(path);
|
||||
const [root, ...parts] = reducePathComponents(getPathComponents(path));
|
||||
if (parts.length) {
|
||||
const joinedParts = root + parts.join(directorySeparator);
|
||||
return { path: hasTrailingDirectorySeparator(path) ? joinedParts + directorySeparator : joinedParts, parts };
|
||||
}
|
||||
else {
|
||||
return { path: root, parts };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path except for its basename. Eg:
|
||||
*
|
||||
* /path/to/file.ext -> /path/to
|
||||
*/
|
||||
export function getDirectoryPath(path: Path): Path;
|
||||
export function getDirectoryPath(path: string): string;
|
||||
export function getDirectoryPath(path: string): string {
|
||||
return path.substr(0, Math.max(getRootLength(path), path.lastIndexOf(directorySeparator)));
|
||||
}
|
||||
|
||||
export function isUrl(path: string) {
|
||||
return path && !isRootedDiskPath(path) && stringContains(path, "://");
|
||||
}
|
||||
|
||||
export function pathIsRelative(path: string): boolean {
|
||||
return /^\.\.?($|[\\/])/.test(path);
|
||||
}
|
||||
|
||||
export function getEmitScriptTarget(compilerOptions: CompilerOptions) {
|
||||
return compilerOptions.target || ScriptTarget.ES3;
|
||||
}
|
||||
@ -2060,19 +1981,207 @@ namespace ts {
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Paths
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* Internally, we represent paths as strings with '/' as the directory separator.
|
||||
* When we make system calls (eg: LanguageServiceHost.getDirectory()),
|
||||
* we expect the host to correctly handle paths in our specified format.
|
||||
*/
|
||||
export const directorySeparator = "/";
|
||||
const altDirectorySeparator = "\\";
|
||||
const urlSchemeSeparator = "://";
|
||||
|
||||
const backslashRegExp = /\\/g;
|
||||
|
||||
/**
|
||||
* Normalize path separators.
|
||||
*/
|
||||
export function normalizeSlashes(path: string): string {
|
||||
return path.replace(backslashRegExp, directorySeparator);
|
||||
}
|
||||
|
||||
function isVolumeCharacter(charCode: number) {
|
||||
return (charCode >= CharacterCodes.a && charCode <= CharacterCodes.z) ||
|
||||
(charCode >= CharacterCodes.A && charCode <= CharacterCodes.Z);
|
||||
}
|
||||
|
||||
function getFileUrlVolumeSeparatorEnd(url: string, start: number) {
|
||||
const ch0 = url.charCodeAt(start);
|
||||
if (ch0 === CharacterCodes.colon) return start + 1;
|
||||
if (ch0 === CharacterCodes.percent && url.charCodeAt(start + 1) === CharacterCodes._3) {
|
||||
const ch2 = url.charCodeAt(start + 2);
|
||||
if (ch2 === CharacterCodes.a || ch2 === CharacterCodes.A) return start + 3;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files").
|
||||
* If the root is part of a URL, the twos-complement of the root length is returned.
|
||||
*/
|
||||
function getEncodedRootLength(path: string): number {
|
||||
if (!path) return 0;
|
||||
const ch0 = path.charCodeAt(0);
|
||||
|
||||
// POSIX or UNC
|
||||
if (ch0 === CharacterCodes.slash || ch0 === CharacterCodes.backslash) {
|
||||
if (path.charCodeAt(1) !== ch0) return 1; // POSIX: "/" (or non-normalized "\")
|
||||
|
||||
const p1 = path.indexOf(ch0 === CharacterCodes.slash ? directorySeparator : altDirectorySeparator, 2);
|
||||
if (p1 < 0) return path.length; // UNC: "//server" or "\\server"
|
||||
|
||||
return p1 + 1; // UNC: "//server/" or "\\server\"
|
||||
}
|
||||
|
||||
// DOS
|
||||
if (isVolumeCharacter(ch0) && path.charCodeAt(1) === CharacterCodes.colon) {
|
||||
const ch2 = path.charCodeAt(2);
|
||||
if (ch2 === CharacterCodes.slash || ch2 === CharacterCodes.backslash) return 3; // DOS: "c:/" or "c:\"
|
||||
if (path.length === 2) return 2; // DOS: "c:" (but not "c:d")
|
||||
}
|
||||
|
||||
// URL
|
||||
const schemeEnd = path.indexOf(urlSchemeSeparator);
|
||||
if (schemeEnd !== -1) {
|
||||
const authorityStart = schemeEnd + urlSchemeSeparator.length;
|
||||
const authorityEnd = path.indexOf(directorySeparator, authorityStart);
|
||||
if (authorityEnd !== -1) { // URL: "file:///", "file://server/", "file://server/path"
|
||||
// For local "file" URLs, include the leading DOS volume (if present).
|
||||
// Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
|
||||
// special case interpreted as "the machine from which the URL is being interpreted".
|
||||
const scheme = path.slice(0, schemeEnd);
|
||||
const authority = path.slice(authorityStart, authorityEnd);
|
||||
if (scheme === "file" && (authority === "" || authority === "localhost") &&
|
||||
isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
|
||||
const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
|
||||
if (volumeSeparatorEnd !== -1) {
|
||||
if (path.charCodeAt(volumeSeparatorEnd) === CharacterCodes.slash) {
|
||||
// URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
|
||||
return ~(volumeSeparatorEnd + 1);
|
||||
}
|
||||
if (volumeSeparatorEnd === path.length) {
|
||||
// URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
|
||||
// but not "file:///c:d" or "file:///c%3ad"
|
||||
return ~volumeSeparatorEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
|
||||
}
|
||||
return ~path.length; // URL: "file://server", "http://server"
|
||||
}
|
||||
|
||||
// relative
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files").
|
||||
*
|
||||
* For example:
|
||||
* ```ts
|
||||
* getRootLength("a") === 0 // ""
|
||||
* getRootLength("/") === 1 // "/"
|
||||
* getRootLength("c:") === 2 // "c:"
|
||||
* getRootLength("c:d") === 0 // ""
|
||||
* getRootLength("c:/") === 3 // "c:/"
|
||||
* getRootLength("c:\\") === 3 // "c:\\"
|
||||
* getRootLength("//server") === 7 // "//server"
|
||||
* getRootLength("//server/share") === 8 // "//server/"
|
||||
* getRootLength("\\\\server") === 7 // "\\\\server"
|
||||
* getRootLength("\\\\server\\share") === 8 // "\\\\server\\"
|
||||
* getRootLength("file:///path") === 8 // "file:///"
|
||||
* getRootLength("file:///c:") === 10 // "file:///c:"
|
||||
* getRootLength("file:///c:d") === 8 // "file:///"
|
||||
* getRootLength("file:///c:/path") === 11 // "file:///c:/"
|
||||
* getRootLength("file://server") === 13 // "file://server"
|
||||
* getRootLength("file://server/path") === 14 // "file://server/"
|
||||
* getRootLength("http://server") === 13 // "http://server"
|
||||
* getRootLength("http://server/path") === 14 // "http://server/"
|
||||
* ```
|
||||
*/
|
||||
export function getRootLength(path: string) {
|
||||
const rootLength = getEncodedRootLength(path);
|
||||
return rootLength < 0 ? ~rootLength : rootLength;
|
||||
}
|
||||
|
||||
// TODO(rbuckton): replace references with `resolvePath`
|
||||
export function normalizePath(path: string): string {
|
||||
return resolvePath(path);
|
||||
}
|
||||
|
||||
export function normalizePathAndParts(path: string): { path: string, parts: string[] } {
|
||||
path = normalizeSlashes(path);
|
||||
const [root, ...parts] = reducePathComponents(getPathComponents(path));
|
||||
if (parts.length) {
|
||||
const joinedParts = root + parts.join(directorySeparator);
|
||||
return { path: hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(joinedParts) : joinedParts, parts };
|
||||
}
|
||||
else {
|
||||
return { path: root, parts };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path except for its basename. Semantics align with NodeJS's `path.dirname`
|
||||
* except that we support URL's as well.
|
||||
*
|
||||
* ```js
|
||||
* getDirectoryPath("/path/to/file.ext") === "/path/to"
|
||||
* getDirectoryPath("/path/to/") === "/path"
|
||||
* getDirectoryPath("/") === "/"
|
||||
* ```
|
||||
*/
|
||||
export function getDirectoryPath(path: Path): Path;
|
||||
/**
|
||||
* Returns the path except for its basename. Semantics align with NodeJS's `path.dirname`
|
||||
* except that we support URL's as well.
|
||||
*
|
||||
* ```js
|
||||
* getDirectoryPath("/path/to/file.ext") === "/path/to"
|
||||
* getDirectoryPath("/path/to/") === "/path"
|
||||
* getDirectoryPath("/") === "/"
|
||||
* ```
|
||||
*/
|
||||
export function getDirectoryPath(path: string): string;
|
||||
export function getDirectoryPath(path: string): string {
|
||||
path = normalizeSlashes(path);
|
||||
|
||||
// If the path provided is itself the root, then return it.
|
||||
const rootLength = getRootLength(path);
|
||||
if (rootLength === path.length) return path;
|
||||
|
||||
// return the leading portion of the path up to the last (non-terminal) directory separator
|
||||
// but not including any trailing directory separator.
|
||||
path = removeTrailingDirectorySeparator(path);
|
||||
return path.slice(0, Math.max(rootLength, path.lastIndexOf(directorySeparator)));
|
||||
}
|
||||
|
||||
export function isUrl(path: string) {
|
||||
return getEncodedRootLength(path) < 0;
|
||||
}
|
||||
|
||||
export function pathIsRelative(path: string): boolean {
|
||||
return /^\.\.?($|[\\/])/.test(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
return getEncodedRootLength(path) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path consists only of a path root.
|
||||
*/
|
||||
export function isDiskPathRoot(path: string) {
|
||||
const rootLength = getRootLength(path);
|
||||
const rootLength = getEncodedRootLength(path);
|
||||
return rootLength > 0 && rootLength === path.length;
|
||||
}
|
||||
|
||||
@ -2101,7 +2210,12 @@ namespace ts {
|
||||
return pathComponents(path, rootLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce an array of path components to a more simplified path by navigating any
|
||||
* `"."` or `".."` entries in the path.
|
||||
*/
|
||||
export function reducePathComponents(components: ReadonlyArray<string>) {
|
||||
if (!some(components)) return [];
|
||||
const reduced = [components[0]];
|
||||
for (let i = 1; i < components.length; i++) {
|
||||
const component = components[i];
|
||||
@ -2131,137 +2245,140 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function getNormalizedAbsolutePath(fileName: string, currentDirectory: string) {
|
||||
return getNormalizedPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory));
|
||||
return getPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a parsed path consisting of a root component and zero or more path segments.
|
||||
* Formats a parsed path consisting of a root component (at index 0) and zero or more path
|
||||
* segments (at indices > 0).
|
||||
*/
|
||||
export function getNormalizedPathFromPathComponents(pathComponents: ReadonlyArray<string>) {
|
||||
if (pathComponents && pathComponents.length) {
|
||||
return pathComponents[0] + pathComponents.slice(1).join(directorySeparator);
|
||||
}
|
||||
return "";
|
||||
export function getPathFromPathComponents(pathComponents: ReadonlyArray<string>) {
|
||||
if (pathComponents.length === 0) return "";
|
||||
|
||||
const root = pathComponents[0] && ensureTrailingDirectorySeparator(pathComponents[0]);
|
||||
if (pathComponents.length === 1) return root;
|
||||
|
||||
return root + pathComponents.slice(1).join(directorySeparator);
|
||||
}
|
||||
|
||||
function getNormalizedPathComponentsOfUrl(url: string) {
|
||||
// Get root length of http://www.website.com/folder1/folder2/
|
||||
// In this example the root is: http://www.website.com/
|
||||
// normalized path components should be ["http://www.website.com/", "folder1", "folder2"]
|
||||
function getPathComponentsRelativeTo(from: string, to: string, stringEqualityComparer: (a: string, b: string) => boolean, getCanonicalFileName: GetCanonicalFileName) {
|
||||
const fromComponents = reducePathComponents(getPathComponents(from));
|
||||
const toComponents = reducePathComponents(getPathComponents(to));
|
||||
|
||||
const urlLength = url.length;
|
||||
// Initial root length is http:// part
|
||||
let rootLength = url.indexOf("://") + "://".length;
|
||||
while (rootLength < urlLength) {
|
||||
// Consume all immediate slashes in the protocol
|
||||
// eg.initial rootlength is just file:// but it needs to consume another "/" in file:///
|
||||
if (url.charCodeAt(rootLength) === CharacterCodes.slash) {
|
||||
rootLength++;
|
||||
}
|
||||
else {
|
||||
// non slash character means we continue proceeding to next component of root search
|
||||
break;
|
||||
}
|
||||
let start: number;
|
||||
for (start = 0; start < fromComponents.length && start < toComponents.length; start++) {
|
||||
const fromComponent = getCanonicalFileName(fromComponents[start]);
|
||||
const toComponent = getCanonicalFileName(toComponents[start]);
|
||||
const comparer = start === 0 ? equateStringsCaseInsensitive : stringEqualityComparer;
|
||||
if (!comparer(fromComponent, toComponent)) break;
|
||||
}
|
||||
|
||||
// there are no parts after http:// just return current string as the pathComponent
|
||||
if (rootLength === urlLength) {
|
||||
return [url];
|
||||
if (start === 0) {
|
||||
return toComponents;
|
||||
}
|
||||
|
||||
// Find the index of "/" after website.com so the root can be http://www.website.com/ (from existing http://)
|
||||
const indexOfNextSlash = url.indexOf(directorySeparator, rootLength);
|
||||
if (indexOfNextSlash !== -1) {
|
||||
// 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 reducePathComponents(pathComponents(url, rootLength));
|
||||
}
|
||||
else {
|
||||
// Can't find the host assume the rest of the string as component
|
||||
// but make sure we append "/" to it as root is not joined using "/"
|
||||
// eg. if url passed in was http://website.com we want to use root as [http://website.com/]
|
||||
// so that other path manipulations will be correct and it can be merged with relative paths correctly
|
||||
return [url + directorySeparator];
|
||||
const components = toComponents.slice(start);
|
||||
const relative: string[] = [];
|
||||
for (; start < fromComponents.length; start++) {
|
||||
relative.push("..");
|
||||
}
|
||||
return ["", ...relative, ...components];
|
||||
}
|
||||
|
||||
function getNormalizedPathOrUrlComponents(pathOrUrl: string, currentDirectory: string) {
|
||||
if (isUrl(pathOrUrl)) {
|
||||
return getNormalizedPathComponentsOfUrl(pathOrUrl);
|
||||
}
|
||||
else {
|
||||
return getNormalizedPathComponents(pathOrUrl, currentDirectory);
|
||||
}
|
||||
/**
|
||||
* Gets a relative path that can be used to traverse between `from` and `to`.
|
||||
*/
|
||||
export function getRelativePath(from: string, to: string, ignoreCase: boolean): string;
|
||||
/**
|
||||
* Gets a relative path that can be used to traverse between `from` and `to`.
|
||||
*/
|
||||
// tslint:disable-next-line:unified-signatures
|
||||
export function getRelativePath(from: string, to: string, getCanonicalFileName: GetCanonicalFileName): string;
|
||||
export function getRelativePath(from: string, to: string, getCanonicalFileNameOrIgnoreCase: GetCanonicalFileName | boolean) {
|
||||
Debug.assert((getRootLength(from) > 0) === (getRootLength(to) > 0), "Paths must either both be absolute or both be relative");
|
||||
const getCanonicalFileName = typeof getCanonicalFileNameOrIgnoreCase === "function" ? getCanonicalFileNameOrIgnoreCase : identity;
|
||||
const ignoreCase = typeof getCanonicalFileNameOrIgnoreCase === "boolean" ? getCanonicalFileNameOrIgnoreCase : false;
|
||||
const pathComponents = getPathComponentsRelativeTo(from, to, ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive, getCanonicalFileName);
|
||||
return getPathFromPathComponents(pathComponents);
|
||||
}
|
||||
|
||||
export function getRelativePathToDirectoryOrUrl(directoryPathOrUrl: string, relativeOrAbsolutePath: string, currentDirectory: string, getCanonicalFileName: GetCanonicalFileName, isAbsolutePathAnUrl: boolean) {
|
||||
const pathComponents = getNormalizedPathOrUrlComponents(relativeOrAbsolutePath, currentDirectory);
|
||||
const directoryComponents = getNormalizedPathOrUrlComponents(directoryPathOrUrl, currentDirectory);
|
||||
if (directoryComponents.length > 1 && lastOrUndefined(directoryComponents) === "") {
|
||||
// If the directory path given was of type test/cases/ then we really need components of directory to be only till its name
|
||||
// that is ["test", "cases", ""] needs to be actually ["test", "cases"]
|
||||
directoryComponents.pop();
|
||||
const pathComponents = getPathComponentsRelativeTo(
|
||||
resolvePath(currentDirectory, directoryPathOrUrl),
|
||||
resolvePath(currentDirectory, relativeOrAbsolutePath),
|
||||
equateStringsCaseSensitive,
|
||||
getCanonicalFileName
|
||||
);
|
||||
|
||||
const firstComponent = pathComponents[0];
|
||||
if (isAbsolutePathAnUrl && isRootedDiskPath(firstComponent)) {
|
||||
const prefix = firstComponent.charAt(0) === directorySeparator ? "file://" : "file:///";
|
||||
pathComponents[0] = prefix + firstComponent;
|
||||
}
|
||||
|
||||
// Find the component that differs
|
||||
let joinStartIndex: number;
|
||||
for (joinStartIndex = 0; joinStartIndex < pathComponents.length && joinStartIndex < directoryComponents.length; joinStartIndex++) {
|
||||
if (getCanonicalFileName(directoryComponents[joinStartIndex]) !== getCanonicalFileName(pathComponents[joinStartIndex])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the relative path
|
||||
if (joinStartIndex) {
|
||||
let relativePath = "";
|
||||
const relativePathComponents = pathComponents.slice(joinStartIndex, pathComponents.length);
|
||||
for (; joinStartIndex < directoryComponents.length; joinStartIndex++) {
|
||||
if (directoryComponents[joinStartIndex] !== "") {
|
||||
relativePath = relativePath + ".." + directorySeparator;
|
||||
}
|
||||
}
|
||||
|
||||
return relativePath + relativePathComponents.join(directorySeparator);
|
||||
}
|
||||
|
||||
// Cant find the relative path, get the absolute path
|
||||
let absolutePath = getNormalizedPathFromPathComponents(pathComponents);
|
||||
if (isAbsolutePathAnUrl && isRootedDiskPath(absolutePath)) {
|
||||
absolutePath = "file:///" + absolutePath;
|
||||
}
|
||||
|
||||
return absolutePath;
|
||||
}
|
||||
|
||||
export function getRelativePath(path: string, directoryPath: string, getCanonicalFileName: GetCanonicalFileName) {
|
||||
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
return ensurePathIsRelative(relativePath);
|
||||
}
|
||||
|
||||
export function ensurePathIsRelative(path: string): string {
|
||||
return !pathIsRelative(path) ? "./" + path : path;
|
||||
}
|
||||
|
||||
export function getBaseFileName(path: string) {
|
||||
if (path === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const i = path.lastIndexOf(directorySeparator);
|
||||
return i < 0 ? path : path.substring(i + 1);
|
||||
return getPathFromPathComponents(pathComponents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines two paths. If a path is absolute, it replaces any previous path.
|
||||
* Ensures a path is either absolute (prefixed with `/` or `c:`) or dot-relative (prefixed
|
||||
* with `./` or `../`) so as not to be confused with an unprefixed module name.
|
||||
*/
|
||||
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 (hasTrailingDirectorySeparator(path1)) return path1 + path2;
|
||||
return path1 + directorySeparator + path2;
|
||||
export function ensurePathIsNonModuleName(path: string): string {
|
||||
return getRootLength(path) === 0 && !pathIsRelative(path) ? "./" + path : path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the portion of a path following the last separator (`/`).
|
||||
* Semantics align with NodeJS's `path.basename` except that we support URL's as well.
|
||||
*/
|
||||
export function getBaseFileName(path: string): string;
|
||||
/**
|
||||
* Gets the portion of a path following the last separator (`/`).
|
||||
* Semantics align with NodeJS's `path.basename` except that we support URL's as well.
|
||||
* If the base name has any one of the provided extensions, it is removed.
|
||||
*/
|
||||
export function getBaseFileName(path: string, extensions: string | ReadonlyArray<string>, ignoreCase: boolean): string;
|
||||
export function getBaseFileName(path: string, extensions?: string | ReadonlyArray<string>, ignoreCase?: boolean) {
|
||||
path = normalizeSlashes(path);
|
||||
|
||||
// if the path provided is itself the root, then it has not file name.
|
||||
const rootLength = getRootLength(path);
|
||||
if (rootLength === path.length) return "";
|
||||
|
||||
// return the trailing portion of the path starting after the last (non-terminal) directory
|
||||
// separator but not including any trailing directory separator.
|
||||
path = removeTrailingDirectorySeparator(path);
|
||||
const name = path.slice(Math.max(getRootLength(path), path.lastIndexOf(directorySeparator) + 1));
|
||||
const extension = extensions !== undefined && ignoreCase !== undefined ? getAnyExtensionFromPath(name, extensions, ignoreCase) : undefined;
|
||||
return extension ? name.slice(0, name.length - extension.length) : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines paths. If a path is absolute, it replaces any previous path.
|
||||
*/
|
||||
export function combinePaths(path: string, ...paths: string[]): string {
|
||||
if (path) path = normalizeSlashes(path);
|
||||
for (let relativePath of paths) {
|
||||
if (!relativePath) continue;
|
||||
relativePath = normalizeSlashes(relativePath);
|
||||
if (!path || getRootLength(relativePath) !== 0) {
|
||||
path = relativePath;
|
||||
}
|
||||
else {
|
||||
path = ensureTrailingDirectorySeparator(path) + relativePath;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines and resolves paths. If a path is absolute, it replaces any previous path. Any
|
||||
* `.` and `..` path components are resolved.
|
||||
*/
|
||||
export function resolvePath(path: string, ...paths: string[]): string {
|
||||
const combined = some(paths) ? combinePaths(path, ...paths) : normalizeSlashes(path);
|
||||
const normalized = getPathFromPathComponents(reducePathComponents(getPathComponents(combined)));
|
||||
return normalized && hasTrailingDirectorySeparator(combined) ? ensureTrailingDirectorySeparator(normalized) : normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2301,40 +2418,71 @@ namespace ts {
|
||||
return path;
|
||||
}
|
||||
|
||||
export function comparePaths(a: string, b: string, currentDirectory: string, ignoreCase?: boolean) {
|
||||
function comparePathsWorker(a: string, b: string, componentComparer: (a: string, b: string) => Comparison) {
|
||||
if (a === b) return Comparison.EqualTo;
|
||||
if (a === undefined) return Comparison.LessThan;
|
||||
if (b === undefined) return Comparison.GreaterThan;
|
||||
a = removeTrailingDirectorySeparator(a);
|
||||
b = removeTrailingDirectorySeparator(b);
|
||||
const aComponents = getNormalizedPathComponents(a, currentDirectory);
|
||||
const bComponents = getNormalizedPathComponents(b, currentDirectory);
|
||||
const aComponents = reducePathComponents(getPathComponents(a));
|
||||
const bComponents = reducePathComponents(getPathComponents(b));
|
||||
const sharedLength = Math.min(aComponents.length, bComponents.length);
|
||||
const comparer = ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive;
|
||||
for (let i = 0; i < sharedLength; i++) {
|
||||
const result = comparer(aComponents[i], bComponents[i]);
|
||||
const stringComparer = i === 0 ? compareStringsCaseInsensitive : componentComparer;
|
||||
const result = stringComparer(aComponents[i], bComponents[i]);
|
||||
if (result !== Comparison.EqualTo) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return compareValues(aComponents.length, bComponents.length);
|
||||
}
|
||||
|
||||
export function containsPath(parent: string, child: string, currentDirectory: string, ignoreCase?: boolean) {
|
||||
/**
|
||||
* Performs a case-sensitive comparison of two paths.
|
||||
*/
|
||||
export function comparePathsCaseSensitive(a: string, b: string) {
|
||||
return comparePathsWorker(a, b, compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a case-insensitive comparison of two paths.
|
||||
*/
|
||||
export function comparePathsCaseInsensitive(a: string, b: string) {
|
||||
return comparePathsWorker(a, b, compareStringsCaseInsensitive);
|
||||
}
|
||||
|
||||
export function comparePaths(a: string, b: string, ignoreCase?: boolean): Comparison;
|
||||
export function comparePaths(a: string, b: string, currentDirectory: string, ignoreCase?: boolean): Comparison;
|
||||
export function comparePaths(a: string, b: string, currentDirectory?: string | boolean, ignoreCase?: boolean) {
|
||||
if (typeof currentDirectory === "string") {
|
||||
a = combinePaths(currentDirectory, a);
|
||||
b = combinePaths(currentDirectory, b);
|
||||
}
|
||||
else if (typeof currentDirectory === "boolean") {
|
||||
ignoreCase = currentDirectory;
|
||||
}
|
||||
return comparePathsWorker(a, b, ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
export function containsPath(parent: string, child: string, ignoreCase?: boolean): boolean;
|
||||
export function containsPath(parent: string, child: string, currentDirectory: string, ignoreCase?: boolean): boolean;
|
||||
export function containsPath(parent: string, child: string, currentDirectory?: string | boolean, ignoreCase?: boolean) {
|
||||
if (typeof currentDirectory === "string") {
|
||||
parent = combinePaths(currentDirectory, parent);
|
||||
child = combinePaths(currentDirectory, child);
|
||||
}
|
||||
else if (typeof currentDirectory === "boolean") {
|
||||
ignoreCase = currentDirectory;
|
||||
}
|
||||
if (parent === undefined || child === undefined) return false;
|
||||
if (parent === child) return true;
|
||||
parent = removeTrailingDirectorySeparator(parent);
|
||||
child = removeTrailingDirectorySeparator(child);
|
||||
if (parent === child) return true;
|
||||
const parentComponents = getNormalizedPathComponents(parent, currentDirectory);
|
||||
const childComponents = getNormalizedPathComponents(child, currentDirectory);
|
||||
const parentComponents = reducePathComponents(getPathComponents(parent));
|
||||
const childComponents = reducePathComponents(getPathComponents(child));
|
||||
if (childComponents.length < parentComponents.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const equalityComparer = ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive;
|
||||
const componentEqualityComparer = ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive;
|
||||
for (let i = 0; i < parentComponents.length; i++) {
|
||||
const equalityComparer = i === 0 ? equateStringsCaseInsensitive : componentEqualityComparer;
|
||||
if (!equalityComparer(parentComponents[i], childComponents[i])) {
|
||||
return false;
|
||||
}
|
||||
@ -2816,7 +2964,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function changeExtension<T extends string | Path>(path: T, newExtension: string): T {
|
||||
return <T>(removeFileExtension(path) + newExtension);
|
||||
return <T>changeAnyExtension(path, newExtension, extensionsToRemove, /*ignoreCase*/ false);
|
||||
}
|
||||
|
||||
export function changeAnyExtension(path: string, ext: string): string;
|
||||
export function changeAnyExtension(path: string, ext: string, extensions: string | ReadonlyArray<string>, ignoreCase: boolean): string;
|
||||
export function changeAnyExtension(path: string, ext: string, extensions?: string | ReadonlyArray<string>, ignoreCase?: boolean) {
|
||||
const pathext = extensions !== undefined && ignoreCase !== undefined ? getAnyExtensionFromPath(path, extensions, ignoreCase) : getAnyExtensionFromPath(path);
|
||||
return pathext ? path.slice(0, path.length - pathext.length) + (ts.startsWith(ext, ".") ? ext : "." + ext) : path;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3150,14 +3305,40 @@ namespace ts {
|
||||
return find<Extension>(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e));
|
||||
}
|
||||
|
||||
// Retrieves any string from the final "." onwards from a base file name.
|
||||
// Unlike extensionFromPath, which throws an exception on unrecognized extensions.
|
||||
export function getAnyExtensionFromPath(path: string): string | undefined {
|
||||
function getAnyExtensionFromPathWorker(path: string, extensions: string | ReadonlyArray<string>, stringEqualityComparer: (a: string, b: string) => boolean) {
|
||||
if (typeof extensions === "string") extensions = [extensions];
|
||||
for (let extension of extensions) {
|
||||
if (!startsWith(extension, ".")) extension = "." + extension;
|
||||
if (path.length >= extension.length && path.charAt(path.length - extension.length) === ".") {
|
||||
const pathExtension = path.slice(path.length - extension.length);
|
||||
if (stringEqualityComparer(pathExtension, extension)) {
|
||||
return pathExtension;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file extension for a path.
|
||||
*/
|
||||
export function getAnyExtensionFromPath(path: string): string;
|
||||
/**
|
||||
* Gets the file extension for a path, provided it is one of the provided extensions.
|
||||
*/
|
||||
export function getAnyExtensionFromPath(path: string, extensions: string | ReadonlyArray<string>, ignoreCase: boolean): string;
|
||||
export function getAnyExtensionFromPath(path: string, extensions?: string | ReadonlyArray<string>, ignoreCase?: boolean): string {
|
||||
// Retrieves any string from the final "." onwards from a base file name.
|
||||
// Unlike extensionFromPath, which throws an exception on unrecognized extensions.
|
||||
if (extensions) {
|
||||
return getAnyExtensionFromPathWorker(path, extensions, ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive);
|
||||
}
|
||||
const baseFileName = getBaseFileName(path);
|
||||
const extensionIndex = baseFileName.lastIndexOf(".");
|
||||
if (extensionIndex >= 0) {
|
||||
return baseFileName.substring(extensionIndex);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) {
|
||||
|
||||
@ -57,7 +57,7 @@ namespace ts {
|
||||
return currentDirectory;
|
||||
}
|
||||
|
||||
return getNormalizedPathFromPathComponents(commonPathComponents);
|
||||
return getPathFromPathComponents(commonPathComponents);
|
||||
}
|
||||
|
||||
interface OutputFingerprint {
|
||||
|
||||
@ -63,7 +63,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getCachedFileSystemEntries(rootDirPath: Path): MutableFileSystemEntries | undefined {
|
||||
return cachedReadDirectoryResult.get(rootDirPath);
|
||||
return cachedReadDirectoryResult.get(ensureTrailingDirectorySeparator(rootDirPath));
|
||||
}
|
||||
|
||||
function getCachedFileSystemEntriesForBaseDir(path: Path): MutableFileSystemEntries | undefined {
|
||||
@ -80,7 +80,7 @@ namespace ts {
|
||||
directories: host.getDirectories(rootDir) || []
|
||||
};
|
||||
|
||||
cachedReadDirectoryResult.set(rootDirPath, resultFromHost);
|
||||
cachedReadDirectoryResult.set(ensureTrailingDirectorySeparator(rootDirPath), resultFromHost);
|
||||
return resultFromHost;
|
||||
}
|
||||
|
||||
@ -90,6 +90,7 @@ namespace ts {
|
||||
* The host request is done under try catch block to avoid caching incorrect result
|
||||
*/
|
||||
function tryReadDirectory(rootDir: string, rootDirPath: Path): MutableFileSystemEntries | undefined {
|
||||
rootDirPath = ensureTrailingDirectorySeparator(rootDirPath);
|
||||
const cachedResult = getCachedFileSystemEntries(rootDirPath);
|
||||
if (cachedResult) {
|
||||
return cachedResult;
|
||||
@ -100,7 +101,7 @@ namespace ts {
|
||||
}
|
||||
catch (_e) {
|
||||
// If there is exception to read directories, dont cache the result and direct the calls to host
|
||||
Debug.assert(!cachedReadDirectoryResult.has(rootDirPath));
|
||||
Debug.assert(!cachedReadDirectoryResult.has(ensureTrailingDirectorySeparator(rootDirPath)));
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@ -142,7 +143,7 @@ namespace ts {
|
||||
|
||||
function directoryExists(dirPath: string): boolean {
|
||||
const path = toPath(dirPath);
|
||||
return cachedReadDirectoryResult.has(path) || host.directoryExists(dirPath);
|
||||
return cachedReadDirectoryResult.has(ensureTrailingDirectorySeparator(path)) || host.directoryExists(dirPath);
|
||||
}
|
||||
|
||||
function createDirectory(dirPath: string) {
|
||||
|
||||
@ -28,12 +28,14 @@ namespace Harness.Parallel.Worker {
|
||||
(global as any).describe = ((name, callback) => {
|
||||
testList.push({ name, callback, kind: "suite" });
|
||||
}) as Mocha.IContextDefinition;
|
||||
(global as any).describe.skip = ts.noop;
|
||||
(global as any).it = ((name, callback) => {
|
||||
if (!testList) {
|
||||
throw new Error("Tests must occur within a describe block");
|
||||
}
|
||||
testList.push({ name, callback, kind: "test" });
|
||||
}) as Mocha.ITestDefinition;
|
||||
(global as any).it.skip = ts.noop;
|
||||
}
|
||||
|
||||
function setTimeoutAndExecute(timeout: number | undefined, f: () => void) {
|
||||
|
||||
@ -210,7 +210,9 @@ namespace project {
|
||||
const ignoreCase = this.vfs.ignoreCase;
|
||||
const resolutionInfo: ProjectRunnerTestCaseResolutionInfo & ts.CompilerOptions = JSON.parse(JSON.stringify(this.testCase));
|
||||
resolutionInfo.resolvedInputFiles = this.compilerResult.program.getSourceFiles()
|
||||
.map(input => utils.removeTestPathPrefixes(vpath.isAbsolute(input.fileName) ? vpath.relative(cwd, input.fileName, ignoreCase) : input.fileName));
|
||||
.map(({ fileName: input }) => vpath.beneath(vfs.builtFolder, input, this.vfs.ignoreCase) || vpath.beneath(vfs.testLibFolder, input, this.vfs.ignoreCase) ? utils.removeTestPathPrefixes(input) :
|
||||
vpath.isAbsolute(input) ? vpath.relative(cwd, input, ignoreCase) :
|
||||
input);
|
||||
|
||||
resolutionInfo.emittedFiles = this.compilerResult.outputFiles
|
||||
.map(output => output.meta.get("fileName") || output.file)
|
||||
@ -255,7 +257,7 @@ namespace project {
|
||||
nonSubfolderDiskFiles++;
|
||||
}
|
||||
|
||||
const content = output.text.replace(/\/\/?\.src\//g, "/");
|
||||
const content = utils.removeTestPathPrefixes(output.text, /*retainTrailingDirectorySeparator*/ true);
|
||||
Harness.Baseline.runBaseline(this.getBaselineFolder(this.compilerResult.moduleKind) + diskRelativeName, () => content);
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
292
src/harness/unittests/paths.ts
Normal file
292
src/harness/unittests/paths.ts
Normal file
@ -0,0 +1,292 @@
|
||||
describe("core paths", () => {
|
||||
it("normalizeSlashes", () => {
|
||||
assert.strictEqual(ts.normalizeSlashes("a"), "a");
|
||||
assert.strictEqual(ts.normalizeSlashes("a/b"), "a/b");
|
||||
assert.strictEqual(ts.normalizeSlashes("a\\b"), "a/b");
|
||||
assert.strictEqual(ts.normalizeSlashes("\\\\server\\path"), "//server/path");
|
||||
});
|
||||
it("getRootLength", () => {
|
||||
assert.strictEqual(ts.getRootLength("a"), 0);
|
||||
assert.strictEqual(ts.getRootLength("/"), 1);
|
||||
assert.strictEqual(ts.getRootLength("/path"), 1);
|
||||
assert.strictEqual(ts.getRootLength("c:"), 2);
|
||||
assert.strictEqual(ts.getRootLength("c:d"), 0);
|
||||
assert.strictEqual(ts.getRootLength("c:/"), 3);
|
||||
assert.strictEqual(ts.getRootLength("c:\\"), 3);
|
||||
assert.strictEqual(ts.getRootLength("//server"), 8);
|
||||
assert.strictEqual(ts.getRootLength("//server/share"), 9);
|
||||
assert.strictEqual(ts.getRootLength("\\\\server"), 8);
|
||||
assert.strictEqual(ts.getRootLength("\\\\server\\share"), 9);
|
||||
assert.strictEqual(ts.getRootLength("file:///"), 8);
|
||||
assert.strictEqual(ts.getRootLength("file:///path"), 8);
|
||||
assert.strictEqual(ts.getRootLength("file:///c:"), 10);
|
||||
assert.strictEqual(ts.getRootLength("file:///c:d"), 8);
|
||||
assert.strictEqual(ts.getRootLength("file:///c:/path"), 11);
|
||||
assert.strictEqual(ts.getRootLength("file:///c%3a"), 12);
|
||||
assert.strictEqual(ts.getRootLength("file:///c%3ad"), 8);
|
||||
assert.strictEqual(ts.getRootLength("file:///c%3a/path"), 13);
|
||||
assert.strictEqual(ts.getRootLength("file:///c%3A"), 12);
|
||||
assert.strictEqual(ts.getRootLength("file:///c%3Ad"), 8);
|
||||
assert.strictEqual(ts.getRootLength("file:///c%3A/path"), 13);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost"), 16);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/"), 17);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/path"), 17);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/c:"), 19);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/c:d"), 17);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/c:/path"), 20);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/c%3a"), 21);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/c%3ad"), 17);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/c%3a/path"), 22);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/c%3A"), 21);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/c%3Ad"), 17);
|
||||
assert.strictEqual(ts.getRootLength("file://localhost/c%3A/path"), 22);
|
||||
assert.strictEqual(ts.getRootLength("file://server"), 13);
|
||||
assert.strictEqual(ts.getRootLength("file://server/"), 14);
|
||||
assert.strictEqual(ts.getRootLength("file://server/path"), 14);
|
||||
assert.strictEqual(ts.getRootLength("file://server/c:"), 14);
|
||||
assert.strictEqual(ts.getRootLength("file://server/c:d"), 14);
|
||||
assert.strictEqual(ts.getRootLength("file://server/c:/d"), 14);
|
||||
assert.strictEqual(ts.getRootLength("file://server/c%3a"), 14);
|
||||
assert.strictEqual(ts.getRootLength("file://server/c%3ad"), 14);
|
||||
assert.strictEqual(ts.getRootLength("file://server/c%3a/d"), 14);
|
||||
assert.strictEqual(ts.getRootLength("file://server/c%3A"), 14);
|
||||
assert.strictEqual(ts.getRootLength("file://server/c%3Ad"), 14);
|
||||
assert.strictEqual(ts.getRootLength("file://server/c%3A/d"), 14);
|
||||
assert.strictEqual(ts.getRootLength("http://server"), 13);
|
||||
assert.strictEqual(ts.getRootLength("http://server/path"), 14);
|
||||
});
|
||||
it("isUrl", () => {
|
||||
assert.isFalse(ts.isUrl("a"));
|
||||
assert.isFalse(ts.isUrl("/"));
|
||||
assert.isFalse(ts.isUrl("c:"));
|
||||
assert.isFalse(ts.isUrl("c:d"));
|
||||
assert.isFalse(ts.isUrl("c:/"));
|
||||
assert.isFalse(ts.isUrl("c:\\"));
|
||||
assert.isFalse(ts.isUrl("//server"));
|
||||
assert.isFalse(ts.isUrl("//server/share"));
|
||||
assert.isFalse(ts.isUrl("\\\\server"));
|
||||
assert.isFalse(ts.isUrl("\\\\server\\share"));
|
||||
assert.isTrue(ts.isUrl("file:///path"));
|
||||
assert.isTrue(ts.isUrl("file:///c:"));
|
||||
assert.isTrue(ts.isUrl("file:///c:d"));
|
||||
assert.isTrue(ts.isUrl("file:///c:/path"));
|
||||
assert.isTrue(ts.isUrl("file://server"));
|
||||
assert.isTrue(ts.isUrl("file://server/path"));
|
||||
assert.isTrue(ts.isUrl("http://server"));
|
||||
assert.isTrue(ts.isUrl("http://server/path"));
|
||||
});
|
||||
it("isRootedDiskPath", () => {
|
||||
assert.isFalse(ts.isRootedDiskPath("a"));
|
||||
assert.isTrue(ts.isRootedDiskPath("/"));
|
||||
assert.isTrue(ts.isRootedDiskPath("c:"));
|
||||
assert.isFalse(ts.isRootedDiskPath("c:d"));
|
||||
assert.isTrue(ts.isRootedDiskPath("c:/"));
|
||||
assert.isTrue(ts.isRootedDiskPath("c:\\"));
|
||||
assert.isTrue(ts.isRootedDiskPath("//server"));
|
||||
assert.isTrue(ts.isRootedDiskPath("//server/share"));
|
||||
assert.isTrue(ts.isRootedDiskPath("\\\\server"));
|
||||
assert.isTrue(ts.isRootedDiskPath("\\\\server\\share"));
|
||||
assert.isFalse(ts.isRootedDiskPath("file:///path"));
|
||||
assert.isFalse(ts.isRootedDiskPath("file:///c:"));
|
||||
assert.isFalse(ts.isRootedDiskPath("file:///c:d"));
|
||||
assert.isFalse(ts.isRootedDiskPath("file:///c:/path"));
|
||||
assert.isFalse(ts.isRootedDiskPath("file://server"));
|
||||
assert.isFalse(ts.isRootedDiskPath("file://server/path"));
|
||||
assert.isFalse(ts.isRootedDiskPath("http://server"));
|
||||
assert.isFalse(ts.isRootedDiskPath("http://server/path"));
|
||||
});
|
||||
it("getDirectoryPath", () => {
|
||||
assert.strictEqual(ts.getDirectoryPath(""), "");
|
||||
assert.strictEqual(ts.getDirectoryPath("a"), "");
|
||||
assert.strictEqual(ts.getDirectoryPath("a/b"), "a");
|
||||
assert.strictEqual(ts.getDirectoryPath("/"), "/");
|
||||
assert.strictEqual(ts.getDirectoryPath("/a"), "/");
|
||||
assert.strictEqual(ts.getDirectoryPath("/a/"), "/");
|
||||
assert.strictEqual(ts.getDirectoryPath("/a/b"), "/a");
|
||||
assert.strictEqual(ts.getDirectoryPath("/a/b/"), "/a");
|
||||
assert.strictEqual(ts.getDirectoryPath("c:"), "c:");
|
||||
assert.strictEqual(ts.getDirectoryPath("c:d"), "");
|
||||
assert.strictEqual(ts.getDirectoryPath("c:/"), "c:/");
|
||||
assert.strictEqual(ts.getDirectoryPath("c:/path"), "c:/");
|
||||
assert.strictEqual(ts.getDirectoryPath("c:/path/"), "c:/");
|
||||
assert.strictEqual(ts.getDirectoryPath("//server"), "//server");
|
||||
assert.strictEqual(ts.getDirectoryPath("//server/"), "//server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("//server/share"), "//server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("//server/share/"), "//server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("\\\\server"), "//server");
|
||||
assert.strictEqual(ts.getDirectoryPath("\\\\server\\"), "//server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("\\\\server\\share"), "//server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("\\\\server\\share\\"), "//server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("file:///"), "file:///");
|
||||
assert.strictEqual(ts.getDirectoryPath("file:///path"), "file:///");
|
||||
assert.strictEqual(ts.getDirectoryPath("file:///path/"), "file:///");
|
||||
assert.strictEqual(ts.getDirectoryPath("file:///c:"), "file:///c:");
|
||||
assert.strictEqual(ts.getDirectoryPath("file:///c:d"), "file:///");
|
||||
assert.strictEqual(ts.getDirectoryPath("file:///c:/"), "file:///c:/");
|
||||
assert.strictEqual(ts.getDirectoryPath("file:///c:/path"), "file:///c:/");
|
||||
assert.strictEqual(ts.getDirectoryPath("file:///c:/path/"), "file:///c:/");
|
||||
assert.strictEqual(ts.getDirectoryPath("file://server"), "file://server");
|
||||
assert.strictEqual(ts.getDirectoryPath("file://server/"), "file://server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("file://server/path"), "file://server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("file://server/path/"), "file://server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("http://server"), "http://server");
|
||||
assert.strictEqual(ts.getDirectoryPath("http://server/"), "http://server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("http://server/path"), "http://server/");
|
||||
assert.strictEqual(ts.getDirectoryPath("http://server/path/"), "http://server/");
|
||||
});
|
||||
it("getBaseFileName", () => {
|
||||
assert.strictEqual(ts.getBaseFileName(""), "");
|
||||
assert.strictEqual(ts.getBaseFileName("a"), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("a/"), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("/"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("/a"), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("/a/"), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("/a/b"), "b");
|
||||
assert.strictEqual(ts.getBaseFileName("c:"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("c:d"), "c:d");
|
||||
assert.strictEqual(ts.getBaseFileName("c:/"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("c:\\"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("c:/path"), "path");
|
||||
assert.strictEqual(ts.getBaseFileName("c:/path/"), "path");
|
||||
assert.strictEqual(ts.getBaseFileName("//server"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("//server/"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("//server/share"), "share");
|
||||
assert.strictEqual(ts.getBaseFileName("//server/share/"), "share");
|
||||
assert.strictEqual(ts.getBaseFileName("file:///"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("file:///path"), "path");
|
||||
assert.strictEqual(ts.getBaseFileName("file:///path/"), "path");
|
||||
assert.strictEqual(ts.getBaseFileName("file:///c:"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("file:///c:/"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("file:///c:d"), "c:d");
|
||||
assert.strictEqual(ts.getBaseFileName("file:///c:/d"), "d");
|
||||
assert.strictEqual(ts.getBaseFileName("file:///c:/d/"), "d");
|
||||
assert.strictEqual(ts.getBaseFileName("http://server"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("http://server/"), "");
|
||||
assert.strictEqual(ts.getBaseFileName("http://server/a"), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("http://server/a/"), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("/path/a.ext", ".ext", /*ignoreCase*/ false), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("/path/a.ext", ".EXT", /*ignoreCase*/ true), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("/path/a.ext", "ext", /*ignoreCase*/ false), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("/path/a.b", ".ext", /*ignoreCase*/ false), "a.b");
|
||||
assert.strictEqual(ts.getBaseFileName("/path/a.b", [".b", ".c"], /*ignoreCase*/ false), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("/path/a.c", [".b", ".c"], /*ignoreCase*/ false), "a");
|
||||
assert.strictEqual(ts.getBaseFileName("/path/a.d", [".b", ".c"], /*ignoreCase*/ false), "a.d");
|
||||
});
|
||||
it("getAnyExtensionFromPath", () => {
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath(""), "");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath(".ext"), ".ext");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath("a.ext"), ".ext");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath("/a.ext"), ".ext");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath("a.ext/"), ".ext");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath("a.ext", ".ext", /*ignoreCase*/ false), ".ext");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath("a.ext", ".EXT", /*ignoreCase*/ true), ".ext");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath("a.ext", "ext", /*ignoreCase*/ false), ".ext");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath("a.b", ".ext", /*ignoreCase*/ false), "");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath("a.b", [".b", ".c"], /*ignoreCase*/ false), ".b");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath("a.c", [".b", ".c"], /*ignoreCase*/ false), ".c");
|
||||
assert.strictEqual(ts.getAnyExtensionFromPath("a.d", [".b", ".c"], /*ignoreCase*/ false), "");
|
||||
});
|
||||
it("getPathComponents", () => {
|
||||
assert.deepEqual(ts.getPathComponents(""), [""]);
|
||||
assert.deepEqual(ts.getPathComponents("a"), ["", "a"]);
|
||||
assert.deepEqual(ts.getPathComponents("./a"), ["", ".", "a"]);
|
||||
assert.deepEqual(ts.getPathComponents("/"), ["/"]);
|
||||
assert.deepEqual(ts.getPathComponents("/a"), ["/", "a"]);
|
||||
assert.deepEqual(ts.getPathComponents("/a/"), ["/", "a"]);
|
||||
assert.deepEqual(ts.getPathComponents("c:"), ["c:"]);
|
||||
assert.deepEqual(ts.getPathComponents("c:d"), ["", "c:d"]);
|
||||
assert.deepEqual(ts.getPathComponents("c:/"), ["c:/"]);
|
||||
assert.deepEqual(ts.getPathComponents("c:/path"), ["c:/", "path"]);
|
||||
assert.deepEqual(ts.getPathComponents("//server"), ["//server"]);
|
||||
assert.deepEqual(ts.getPathComponents("//server/"), ["//server/"]);
|
||||
assert.deepEqual(ts.getPathComponents("//server/share"), ["//server/", "share"]);
|
||||
assert.deepEqual(ts.getPathComponents("file:///"), ["file:///"]);
|
||||
assert.deepEqual(ts.getPathComponents("file:///path"), ["file:///", "path"]);
|
||||
assert.deepEqual(ts.getPathComponents("file:///c:"), ["file:///c:"]);
|
||||
assert.deepEqual(ts.getPathComponents("file:///c:d"), ["file:///", "c:d"]);
|
||||
assert.deepEqual(ts.getPathComponents("file:///c:/"), ["file:///c:/"]);
|
||||
assert.deepEqual(ts.getPathComponents("file:///c:/path"), ["file:///c:/", "path"]);
|
||||
assert.deepEqual(ts.getPathComponents("file://server"), ["file://server"]);
|
||||
assert.deepEqual(ts.getPathComponents("file://server/"), ["file://server/"]);
|
||||
assert.deepEqual(ts.getPathComponents("file://server/path"), ["file://server/", "path"]);
|
||||
assert.deepEqual(ts.getPathComponents("http://server"), ["http://server"]);
|
||||
assert.deepEqual(ts.getPathComponents("http://server/"), ["http://server/"]);
|
||||
assert.deepEqual(ts.getPathComponents("http://server/path"), ["http://server/", "path"]);
|
||||
});
|
||||
it("reducePathComponents", () => {
|
||||
assert.deepEqual(ts.reducePathComponents([]), []);
|
||||
assert.deepEqual(ts.reducePathComponents([""]), [""]);
|
||||
assert.deepEqual(ts.reducePathComponents(["", "."]), [""]);
|
||||
assert.deepEqual(ts.reducePathComponents(["", ".", "a"]), ["", "a"]);
|
||||
assert.deepEqual(ts.reducePathComponents(["", "a", "."]), ["", "a"]);
|
||||
assert.deepEqual(ts.reducePathComponents(["", ".."]), ["", ".."]);
|
||||
assert.deepEqual(ts.reducePathComponents(["", "..", ".."]), ["", "..", ".."]);
|
||||
assert.deepEqual(ts.reducePathComponents(["", "..", ".", ".."]), ["", "..", ".."]);
|
||||
assert.deepEqual(ts.reducePathComponents(["", "a", ".."]), [""]);
|
||||
assert.deepEqual(ts.reducePathComponents(["", "..", "a"]), ["", "..", "a"]);
|
||||
assert.deepEqual(ts.reducePathComponents(["/"]), ["/"]);
|
||||
assert.deepEqual(ts.reducePathComponents(["/", "."]), ["/"]);
|
||||
assert.deepEqual(ts.reducePathComponents(["/", ".."]), ["/"]);
|
||||
assert.deepEqual(ts.reducePathComponents(["/", "a", ".."]), ["/"]);
|
||||
});
|
||||
it("combinePaths", () => {
|
||||
assert.strictEqual(ts.combinePaths("/", "/node_modules/@types"), "/node_modules/@types");
|
||||
assert.strictEqual(ts.combinePaths("/a/..", ""), "/a/..");
|
||||
assert.strictEqual(ts.combinePaths("/a/..", "b"), "/a/../b");
|
||||
assert.strictEqual(ts.combinePaths("/a/..", "b/"), "/a/../b/");
|
||||
assert.strictEqual(ts.combinePaths("/a/..", "/"), "/");
|
||||
assert.strictEqual(ts.combinePaths("/a/..", "/b"), "/b");
|
||||
});
|
||||
it("resolvePath", () => {
|
||||
assert.strictEqual(ts.resolvePath(""), "");
|
||||
assert.strictEqual(ts.resolvePath("."), "");
|
||||
assert.strictEqual(ts.resolvePath("./"), "");
|
||||
assert.strictEqual(ts.resolvePath(".."), "..");
|
||||
assert.strictEqual(ts.resolvePath("../"), "../");
|
||||
assert.strictEqual(ts.resolvePath("/"), "/");
|
||||
assert.strictEqual(ts.resolvePath("/."), "/");
|
||||
assert.strictEqual(ts.resolvePath("/./"), "/");
|
||||
assert.strictEqual(ts.resolvePath("/../"), "/");
|
||||
assert.strictEqual(ts.resolvePath("/a"), "/a");
|
||||
assert.strictEqual(ts.resolvePath("/a/"), "/a/");
|
||||
assert.strictEqual(ts.resolvePath("/a/."), "/a");
|
||||
assert.strictEqual(ts.resolvePath("/a/./"), "/a/");
|
||||
assert.strictEqual(ts.resolvePath("/a/./b"), "/a/b");
|
||||
assert.strictEqual(ts.resolvePath("/a/./b/"), "/a/b/");
|
||||
assert.strictEqual(ts.resolvePath("/a/.."), "/");
|
||||
assert.strictEqual(ts.resolvePath("/a/../"), "/");
|
||||
assert.strictEqual(ts.resolvePath("/a/../b"), "/b");
|
||||
assert.strictEqual(ts.resolvePath("/a/../b/"), "/b/");
|
||||
assert.strictEqual(ts.resolvePath("/a/..", "b"), "/b");
|
||||
assert.strictEqual(ts.resolvePath("/a/..", "/"), "/");
|
||||
assert.strictEqual(ts.resolvePath("/a/..", "b/"), "/b/");
|
||||
assert.strictEqual(ts.resolvePath("/a/..", "/b"), "/b");
|
||||
assert.strictEqual(ts.resolvePath("/a/.", "b"), "/a/b");
|
||||
assert.strictEqual(ts.resolvePath("/a/.", "."), "/a");
|
||||
assert.strictEqual(ts.resolvePath("a", "b", "c"), "a/b/c");
|
||||
assert.strictEqual(ts.resolvePath("a", "b", "/c"), "/c");
|
||||
assert.strictEqual(ts.resolvePath("a", "b", "../c"), "a/c");
|
||||
});
|
||||
it("getPathRelativeTo", () => {
|
||||
assert.strictEqual(ts.getRelativePath("/", "/", /*ignoreCase*/ false), "");
|
||||
assert.strictEqual(ts.getRelativePath("/a", "/a", /*ignoreCase*/ false), "");
|
||||
assert.strictEqual(ts.getRelativePath("/a/", "/a", /*ignoreCase*/ false), "");
|
||||
assert.strictEqual(ts.getRelativePath("/a", "/", /*ignoreCase*/ false), "..");
|
||||
assert.strictEqual(ts.getRelativePath("/a", "/b", /*ignoreCase*/ false), "../b");
|
||||
assert.strictEqual(ts.getRelativePath("/a/b", "/b", /*ignoreCase*/ false), "../../b");
|
||||
assert.strictEqual(ts.getRelativePath("/a/b/c", "/b", /*ignoreCase*/ false), "../../../b");
|
||||
assert.strictEqual(ts.getRelativePath("/a/b/c", "/b/c", /*ignoreCase*/ false), "../../../b/c");
|
||||
assert.strictEqual(ts.getRelativePath("/a/b/c", "/a/b", /*ignoreCase*/ false), "..");
|
||||
assert.strictEqual(ts.getRelativePath("c:", "d:", /*ignoreCase*/ false), "d:/");
|
||||
assert.strictEqual(ts.getRelativePath("file:///", "file:///", /*ignoreCase*/ false), "");
|
||||
assert.strictEqual(ts.getRelativePath("file:///a", "file:///a", /*ignoreCase*/ false), "");
|
||||
assert.strictEqual(ts.getRelativePath("file:///a/", "file:///a", /*ignoreCase*/ false), "");
|
||||
assert.strictEqual(ts.getRelativePath("file:///a", "file:///", /*ignoreCase*/ false), "..");
|
||||
assert.strictEqual(ts.getRelativePath("file:///a", "file:///b", /*ignoreCase*/ false), "../b");
|
||||
assert.strictEqual(ts.getRelativePath("file:///a/b", "file:///b", /*ignoreCase*/ false), "../../b");
|
||||
assert.strictEqual(ts.getRelativePath("file:///a/b/c", "file:///b", /*ignoreCase*/ false), "../../../b");
|
||||
assert.strictEqual(ts.getRelativePath("file:///a/b/c", "file:///b/c", /*ignoreCase*/ false), "../../../b/c");
|
||||
assert.strictEqual(ts.getRelativePath("file:///a/b/c", "file:///a/b", /*ignoreCase*/ false), "..");
|
||||
assert.strictEqual(ts.getRelativePath("file:///c:", "file:///d:", /*ignoreCase*/ false), "file:///d:/");
|
||||
});
|
||||
});
|
||||
@ -27,9 +27,9 @@ namespace utils {
|
||||
}
|
||||
}
|
||||
|
||||
const testPathPrefixRegExp = /\/\.(ts|lib|src)\//g;
|
||||
export function removeTestPathPrefixes(text: string) {
|
||||
return text !== undefined ? text.replace(testPathPrefixRegExp, "") : undefined;
|
||||
const testPathPrefixRegExp = /(?:(file:\/{3})|\/)\.(ts|lib|src)\//g;
|
||||
export function removeTestPathPrefixes(text: string, retainTrailingDirectorySeparator?: boolean) {
|
||||
return text !== undefined ? text.replace(testPathPrefixRegExp, (_, scheme) => scheme || (retainTrailingDirectorySeparator ? "/" : "")) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -332,16 +332,25 @@ namespace vfs {
|
||||
}
|
||||
}
|
||||
|
||||
private _depth: string[] = [];
|
||||
|
||||
/**
|
||||
* Make a directory and all of its parent paths (if they don't exist).
|
||||
*/
|
||||
public mkdirpSync(path: string) {
|
||||
path = this._resolve(path);
|
||||
try {
|
||||
this._depth.push(path);
|
||||
path = this._resolve(path);
|
||||
this.mkdirSync(path);
|
||||
}
|
||||
catch (e) {
|
||||
if (e.code === "ENOENT") {
|
||||
if (this._depth.length > 10) {
|
||||
console.log(`path: ${path}`);
|
||||
console.log(`dirname: ${vpath.dirname(path)}`);
|
||||
console.log(this._depth);
|
||||
throw e;
|
||||
}
|
||||
this.mkdirpSync(vpath.dirname(path));
|
||||
this.mkdirSync(path);
|
||||
}
|
||||
@ -349,6 +358,9 @@ namespace vfs {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this._depth.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,7 +1,33 @@
|
||||
namespace vpath {
|
||||
export import sep = ts.directorySeparator;
|
||||
export import normalizeSeparators = ts.normalizeSlashes;
|
||||
export import isAbsolute = ts.isRootedDiskPath;
|
||||
export import isRoot = ts.isDiskPathRoot;
|
||||
export import hasTrailingSeparator = ts.hasTrailingDirectorySeparator;
|
||||
export import addTrailingSeparator = ts.ensureTrailingDirectorySeparator;
|
||||
export import removeTrailingSeparator = ts.removeTrailingDirectorySeparator;
|
||||
export import normalize = ts.normalizePath;
|
||||
export import combine = ts.combinePaths;
|
||||
export import parse = ts.getPathComponents;
|
||||
export import reduce = ts.reducePathComponents;
|
||||
export import format = ts.getPathFromPathComponents;
|
||||
export import resolve = ts.resolvePath;
|
||||
export import compare = ts.comparePaths;
|
||||
export import compareCaseSensitive = ts.comparePathsCaseSensitive;
|
||||
export import compareCaseInsensitive = ts.comparePathsCaseInsensitive;
|
||||
export import dirname = ts.getDirectoryPath;
|
||||
export import basename = ts.getBaseFileName;
|
||||
export import extname = ts.getAnyExtensionFromPath;
|
||||
export import relative = ts.getRelativePath;
|
||||
export import beneath = ts.containsPath;
|
||||
export import changeExtension = ts.changeAnyExtension;
|
||||
export import isTypeScript = ts.hasTypeScriptFileExtension;
|
||||
export import isJavaScript = ts.hasJavaScriptFileExtension;
|
||||
|
||||
const invalidRootComponentRegExp = /^(?!(\/|\/\/\w+\/|[a-zA-Z]:\/?|)$)/;
|
||||
const invalidNavigableComponentRegExp = /[:*?"<>|]/;
|
||||
const invalidNonNavigableComponentRegExp = /^\.{1,2}$|[:*?"<>|]/;
|
||||
const extRegExp = /\.\w+$/;
|
||||
|
||||
export const enum ValidationFlags {
|
||||
None = 0,
|
||||
@ -32,17 +58,6 @@ namespace vpath {
|
||||
Basename = RequireBasename | AllowExtname,
|
||||
}
|
||||
|
||||
export function valid(path: string, flags: ValidationFlags = ValidationFlags.RelativeOrAbsolute) {
|
||||
return validateComponents(parse(path), flags, hasTrailingSeparator(path));
|
||||
}
|
||||
|
||||
export function validate(path: string, flags: ValidationFlags = ValidationFlags.RelativeOrAbsolute) {
|
||||
const components = parse(path);
|
||||
const trailing = hasTrailingSeparator(path);
|
||||
if (!validateComponents(components, flags, trailing)) throw vfs.createIOError("ENOENT");
|
||||
return components.length > 1 && trailing ? format(reduce(components)) + sep : format(reduce(components));
|
||||
}
|
||||
|
||||
function validateComponents(components: string[], flags: ValidationFlags, hasTrailingSeparator: boolean) {
|
||||
const hasRoot = !!components[0];
|
||||
const hasDirname = components.length > 2;
|
||||
@ -80,244 +95,11 @@ namespace vpath {
|
||||
return true;
|
||||
}
|
||||
|
||||
import getRootLength = ts.getRootLength;
|
||||
|
||||
export import sep = ts.directorySeparator;
|
||||
export import normalizeSeparators = ts.normalizeSlashes;
|
||||
export import isAbsolute = ts.isRootedDiskPath;
|
||||
export import isRoot = ts.isDiskPathRoot;
|
||||
export import hasTrailingSeparator = ts.hasTrailingDirectorySeparator;
|
||||
export import addTrailingSeparator = ts.ensureTrailingDirectorySeparator;
|
||||
export import removeTrailingSeparator = ts.removeTrailingDirectorySeparator;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Combines and normalizes two 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
|
||||
// are already absolute and does not perform "canonicalization".
|
||||
function relativeWorker(from: string, to: string, stringEqualityComparer: (a: string, b: string) => boolean) {
|
||||
if (!isAbsolute(from)) throw new Error("Path not absolute");
|
||||
if (!isAbsolute(to)) throw new Error("Path not absolute");
|
||||
|
||||
const fromComponents = reduce(parse(from));
|
||||
const toComponents = reduce(parse(to));
|
||||
|
||||
let start: number;
|
||||
for (start = 0; start < fromComponents.length && start < toComponents.length; start++) {
|
||||
if (!stringEqualityComparer(fromComponents[start], toComponents[start])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (start === 0 || (start === 1 && fromComponents[0] === "/")) {
|
||||
return format(toComponents);
|
||||
}
|
||||
|
||||
const components = toComponents.slice(start);
|
||||
for (; start < fromComponents.length; start++) {
|
||||
components.unshift("..");
|
||||
}
|
||||
|
||||
return format(["", ...components]);
|
||||
}
|
||||
|
||||
function relativeCaseSensitive(from: string, to: string) {
|
||||
return relativeWorker(from, to, ts.equateStringsCaseSensitive);
|
||||
}
|
||||
|
||||
function relativeCaseInsensitive(from: string, to: string) {
|
||||
return relativeWorker(from, to, ts.equateStringsCaseInsensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a relative path that can be used to traverse between `from` and `to`.
|
||||
*/
|
||||
// NOTE: this differs from `ts.getRelativePathToDirectoryOrUrl` in that it requires both paths
|
||||
// are already absolute and does not perform "canonicalization".
|
||||
export function relative(from: string, to: string, ignoreCase: boolean) {
|
||||
return ignoreCase ? relativeCaseInsensitive(from, to) : relativeCaseSensitive(from, to);
|
||||
}
|
||||
|
||||
// NOTE: this differs from `ts.comparePaths` due to the behavior of `parse`.
|
||||
function compareWorker(a: string, b: string, stringComparer: (a: string, b: string) => number) {
|
||||
if (a === b) return 0;
|
||||
a = removeTrailingSeparator(a);
|
||||
b = removeTrailingSeparator(b);
|
||||
if (a === b) return 0;
|
||||
const aComponents = reduce(parse(a));
|
||||
const bComponents = reduce(parse(b));
|
||||
const len = Math.min(aComponents.length, bComponents.length);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const result = stringComparer(aComponents[i], bComponents[i]);
|
||||
if (result !== 0) return result;
|
||||
}
|
||||
return ts.compareValues(aComponents.length, bComponents.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a case-sensitive comparison of two paths.
|
||||
*/
|
||||
export function compareCaseSensitive(a: string, b: string) {
|
||||
return compareWorker(a, b, ts.compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a case-insensitive comparison of two paths.
|
||||
*/
|
||||
export function compareCaseInsensitive(a: string, b: string) {
|
||||
return compareWorker(a, b, ts.compareStringsCaseInsensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two paths.
|
||||
*/
|
||||
// NOTE: this differs from `ts.comparePaths` due to the behavior of `parse`.
|
||||
export function compare(a: string, b: string, ignoreCase: boolean) {
|
||||
return ignoreCase ? compareCaseInsensitive(a, b) : compareCaseSensitive(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether two paths are equal.
|
||||
*/
|
||||
export function equals(a: string, b: string, ignoreCase: boolean) {
|
||||
if (!isAbsolute(a)) throw new Error("Path not absolute");
|
||||
if (!isAbsolute(b)) throw new Error("Path not absolute");
|
||||
if (a === b) return true;
|
||||
a = removeTrailingSeparator(a);
|
||||
b = removeTrailingSeparator(b);
|
||||
if (a === b) return true;
|
||||
a = normalize(a);
|
||||
b = normalize(b);
|
||||
if (a === b) return true;
|
||||
return ignoreCase && a.toUpperCase() === b.toUpperCase();
|
||||
}
|
||||
|
||||
// NOTE: this differs from `ts.containsPath` due to the behavior of `parse`.
|
||||
function beneathWorker(ancestor: string, descendant: string, stringEqualityComparer: (a: string, b: string) => boolean) {
|
||||
if (!isAbsolute(ancestor)) throw new Error("Path not absolute");
|
||||
if (!isAbsolute(descendant)) throw new Error("Path not absolute");
|
||||
const ancestorComponents = reduce(parse(ancestor));
|
||||
const descendantComponents = reduce(parse(descendant));
|
||||
if (descendantComponents.length < ancestorComponents.length) return false;
|
||||
for (let i = 0; i < ancestorComponents.length; i++) {
|
||||
if (!stringEqualityComparer(ancestorComponents[i], descendantComponents[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function beneathCaseSensitive(ancestor: string, descendant: string) {
|
||||
return beneathWorker(ancestor, descendant, ts.equateStringsCaseSensitive);
|
||||
}
|
||||
|
||||
function beneathCaseInsensitive(ancestor: string, descendant: string) {
|
||||
return beneathWorker(ancestor, descendant, ts.equateStringsCaseInsensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the path `descendant` is beneath the path `ancestor`.
|
||||
*/
|
||||
// NOTE: this differs from `containsPath` in compiler/core.ts due to the behavior of `parse`.
|
||||
export function beneath(ancestor: string, descendant: string, ignoreCase: boolean) {
|
||||
return ignoreCase ? beneathCaseInsensitive(ancestor, descendant) : beneathCaseSensitive(ancestor, descendant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent directory name of a path.
|
||||
*/
|
||||
// NOTE: this differs from `ts.getDirectoryPath` due to the behavior of `getRootLength`.
|
||||
export function dirname(path: string) {
|
||||
path = normalizeSeparators(path);
|
||||
path = removeTrailingSeparator(path);
|
||||
return path.substr(0, Math.max(getRootLength(path), path.lastIndexOf(sep)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the portion of a path following the last separator (`/`).
|
||||
*/
|
||||
export function basename(path: string): string;
|
||||
/**
|
||||
* Gets the portion of a path following the last separator (`/`).
|
||||
* If the base name has any one of the provided extensions, it is removed.
|
||||
*/
|
||||
export function basename(path: string, extensions: string | ReadonlyArray<string>, ignoreCase: boolean): string;
|
||||
// NOTE: this differs from `ts.getBaseFileName` in that this function handles extensions in a
|
||||
// fashion similar to the NodeJS `path.basename` function as well as handles case sensitivity.
|
||||
export function basename(path: string, extensions?: string | ReadonlyArray<string>, ignoreCase?: boolean) {
|
||||
path = normalizeSeparators(path);
|
||||
path = removeTrailingSeparator(path);
|
||||
const name = path.substr(Math.max(getRootLength(path), path.lastIndexOf(sep) + 1));
|
||||
const extension = extensions !== undefined && ignoreCase !== undefined ? extname(path, extensions, ignoreCase) : undefined;
|
||||
return extension ? name.slice(0, name.length - extension.length) : name;
|
||||
}
|
||||
|
||||
function extnameWorker(path: string, extensions: string | ReadonlyArray<string>, stringEqualityComparer: (a: string, b: string) => boolean) {
|
||||
const manyExtensions = Array.isArray(extensions) ? extensions : undefined;
|
||||
const singleExtension = Array.isArray(extensions) ? undefined : extensions;
|
||||
const length = manyExtensions ? manyExtensions.length : 1;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let extension = manyExtensions ? manyExtensions[i] : singleExtension;
|
||||
if (!extension.startsWith(".")) extension = "." + extension;
|
||||
if (path.length >= extension.length && path.charAt(path.length - extension.length) === ".") {
|
||||
const pathExtension = path.slice(path.length - extension.length);
|
||||
if (stringEqualityComparer(pathExtension, extension)) {
|
||||
return pathExtension;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
const extRegExp = /\.\w+$/;
|
||||
|
||||
/**
|
||||
* Gets the file extension for a path.
|
||||
*/
|
||||
export function extname(path: string): string;
|
||||
/**
|
||||
* Gets the file extension for a path, provided it is one of the provided extensions.
|
||||
*/
|
||||
export function extname(path: string, extensions: string | ReadonlyArray<string>, ignoreCase: boolean): string;
|
||||
// NOTE: this differs from `ts.getAnyExtensionFromPath` in that this function allows you to
|
||||
// restrict extensions and handle case sensitivity
|
||||
export function extname(path: string, extensions?: string | ReadonlyArray<string>, ignoreCase?: boolean) {
|
||||
if (extensions) {
|
||||
return extnameWorker(path, extensions, ignoreCase ? ts.equateStringsCaseInsensitive : ts.equateStringsCaseSensitive);
|
||||
}
|
||||
|
||||
const match = extRegExp.exec(path);
|
||||
return match ? match[0] : "";
|
||||
}
|
||||
|
||||
export function changeExtension(path: string, ext: string): string;
|
||||
export function changeExtension(path: string, ext: string, extensions: string | ReadonlyArray<string>, ignoreCase: boolean): string;
|
||||
// NOTE: this differs from `ts.changeExtension` in that this function allows you to
|
||||
// specify extensions and handle case sensitivity
|
||||
export function changeExtension(path: string, ext: string, extensions?: string | ReadonlyArray<string>, ignoreCase?: boolean) {
|
||||
const pathext = extensions !== undefined && ignoreCase !== undefined ? extname(path, extensions, ignoreCase) : extname(path);
|
||||
return pathext ? path.slice(0, path.length - pathext.length) + (ext.startsWith(".") ? ext : "." + ext) : path;
|
||||
}
|
||||
|
||||
const typeScriptExtensions: ReadonlyArray<string> = [".ts", ".tsx"];
|
||||
|
||||
export function isTypeScript(path: string) {
|
||||
return extname(path, typeScriptExtensions, /*ignoreCase*/ false).length > 0;
|
||||
}
|
||||
|
||||
const javaScriptExtensions: ReadonlyArray<string> = [".js", ".jsx"];
|
||||
|
||||
export function isJavaScript(path: string) {
|
||||
return extname(path, javaScriptExtensions, /*ignoreCase*/ false).length > 0;
|
||||
export function validate(path: string, flags: ValidationFlags = ValidationFlags.RelativeOrAbsolute) {
|
||||
const components = parse(path);
|
||||
const trailing = hasTrailingSeparator(path);
|
||||
if (!validateComponents(components, flags, trailing)) throw vfs.createIOError("ENOENT");
|
||||
return components.length > 1 && trailing ? format(reduce(components)) + sep : format(reduce(components));
|
||||
}
|
||||
|
||||
export function isDeclaration(path: string) {
|
||||
|
||||
@ -266,7 +266,7 @@ namespace ts.codefix {
|
||||
return [global];
|
||||
}
|
||||
|
||||
const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), moduleResolutionKind, addJsExtension);
|
||||
const relativePath = removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePath(sourceDirectory, moduleFileName, getCanonicalFileName)), moduleResolutionKind, addJsExtension);
|
||||
if (!baseUrl || preferences.importModuleSpecifierPreference === "relative") {
|
||||
return [relativePath];
|
||||
}
|
||||
@ -321,7 +321,7 @@ namespace ts.codefix {
|
||||
1 < 2 = true
|
||||
In this case we should prefer using the relative path "../a" instead of the baseUrl path "foo/a".
|
||||
*/
|
||||
const pathFromSourceToBaseUrl = getRelativePath(baseUrl, sourceDirectory, getCanonicalFileName);
|
||||
const pathFromSourceToBaseUrl = ensurePathIsNonModuleName(getRelativePath(sourceDirectory, baseUrl, getCanonicalFileName));
|
||||
const relativeFirst = getRelativePathNParents(relativePath) < getRelativePathNParents(pathFromSourceToBaseUrl);
|
||||
return relativeFirst ? [relativePath, importRelativeToBaseUrl] : [importRelativeToBaseUrl, relativePath];
|
||||
});
|
||||
@ -343,11 +343,12 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function getRelativePathNParents(relativePath: string): number {
|
||||
let count = 0;
|
||||
for (let i = 0; i + 3 <= relativePath.length && relativePath.slice(i, i + 3) === "../"; i += 3) {
|
||||
count++;
|
||||
const components = getPathComponents(relativePath);
|
||||
if (components[0] || components.length === 1) return 0;
|
||||
for (let i = 1; i < components.length; i++) {
|
||||
if (components[i] !== "..") return i - 1;
|
||||
}
|
||||
return count;
|
||||
return components.length - 1;
|
||||
}
|
||||
|
||||
function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol): string | undefined {
|
||||
@ -389,7 +390,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, rootDirs, getCanonicalFileName);
|
||||
const relativePath = normalizedSourcePath !== undefined ? getRelativePath(normalizedTargetPath, normalizedSourcePath, getCanonicalFileName) : normalizedTargetPath;
|
||||
const relativePath = normalizedSourcePath !== undefined ? ensurePathIsNonModuleName(getRelativePath(normalizedSourcePath, normalizedTargetPath, getCanonicalFileName)) : normalizedTargetPath;
|
||||
return removeFileExtension(relativePath);
|
||||
}
|
||||
|
||||
@ -472,7 +473,7 @@ namespace ts.codefix {
|
||||
return path.substring(parts.topLevelPackageNameIndex + 1);
|
||||
}
|
||||
else {
|
||||
return getRelativePath(path, sourceDirectory, getCanonicalFileName);
|
||||
return ensurePathIsNonModuleName(getRelativePath(sourceDirectory, path, getCanonicalFileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,10 +63,10 @@ namespace ts {
|
||||
|
||||
function getPathUpdater(oldFilePath: string, newFilePath: string, host: LanguageServiceHost): (oldPath: string) => string | undefined {
|
||||
// Get the relative path from old to new location, and append it on to the end of imports and normalize.
|
||||
const rel = getRelativePath(newFilePath, getDirectoryPath(oldFilePath), createGetCanonicalFileName(hostUsesCaseSensitiveFileNames(host)));
|
||||
const rel = ensurePathIsNonModuleName(getRelativePath(getDirectoryPath(oldFilePath), newFilePath, createGetCanonicalFileName(hostUsesCaseSensitiveFileNames(host))));
|
||||
return oldPath => {
|
||||
if (!pathIsRelative(oldPath)) return;
|
||||
return ensurePathIsRelative(normalizePath(combinePaths(getDirectoryPath(oldPath), rel)));
|
||||
return ensurePathIsNonModuleName(normalizePath(combinePaths(getDirectoryPath(oldPath), rel)));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -89,7 +89,9 @@ namespace ts.Completions.PathCompletions {
|
||||
* Remove the basename from the path. Note that we don't use the basename to filter completions;
|
||||
* the client is responsible for refining completions.
|
||||
*/
|
||||
fragment = getDirectoryPath(fragment);
|
||||
if (!hasTrailingDirectorySeparator(fragment)) {
|
||||
fragment = getDirectoryPath(fragment);
|
||||
}
|
||||
|
||||
if (fragment === "") {
|
||||
fragment = "." + directorySeparator;
|
||||
@ -97,8 +99,9 @@ namespace ts.Completions.PathCompletions {
|
||||
|
||||
fragment = ensureTrailingDirectorySeparator(fragment);
|
||||
|
||||
const absolutePath = normalizeAndPreserveTrailingSlash(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment));
|
||||
const baseDirectory = getDirectoryPath(absolutePath);
|
||||
// const absolutePath = normalizeAndPreserveTrailingSlash(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment)); // TODO(rbuckton): should use resolvePaths
|
||||
const absolutePath = resolvePath(scriptPath, fragment);
|
||||
const baseDirectory = hasTrailingDirectorySeparator(absolutePath) ? absolutePath : getDirectoryPath(absolutePath);
|
||||
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
|
||||
|
||||
if (tryDirectoryExists(host, baseDirectory)) {
|
||||
@ -178,7 +181,7 @@ namespace ts.Completions.PathCompletions {
|
||||
}
|
||||
}
|
||||
|
||||
const fragmentDirectory = containsSlash(fragment) ? getDirectoryPath(fragment) : undefined;
|
||||
const fragmentDirectory = containsSlash(fragment) ? hasTrailingDirectorySeparator(fragment) ? fragment : getDirectoryPath(fragment) : undefined;
|
||||
for (const ambientName of getAmbientModuleCompletions(fragment, fragmentDirectory, typeChecker)) {
|
||||
result.push(nameAndKind(ambientName, ScriptElementKind.externalModuleName));
|
||||
}
|
||||
@ -239,14 +242,15 @@ namespace ts.Completions.PathCompletions {
|
||||
|
||||
// The prefix has two effective parts: the directory path and the base component after the filepath that is not a
|
||||
// full directory component. For example: directory/path/of/prefix/base*
|
||||
const normalizedPrefix = normalizeAndPreserveTrailingSlash(parsed.prefix);
|
||||
const normalizedPrefixDirectory = getDirectoryPath(normalizedPrefix);
|
||||
const normalizedPrefixBase = getBaseFileName(normalizedPrefix);
|
||||
const normalizedPrefix = resolvePath(parsed.prefix);
|
||||
const normalizedPrefixDirectory = hasTrailingDirectorySeparator(parsed.prefix) ? normalizedPrefix : getDirectoryPath(normalizedPrefix);
|
||||
const normalizedPrefixBase = hasTrailingDirectorySeparator(parsed.prefix) ? "" : getBaseFileName(normalizedPrefix);
|
||||
|
||||
const fragmentHasPath = containsSlash(fragment);
|
||||
const fragmentDirectory = fragmentHasPath ? hasTrailingDirectorySeparator(fragment) ? fragment : getDirectoryPath(fragment) : undefined;
|
||||
|
||||
// Try and expand the prefix to include any path from the fragment so that we can limit the readDirectory call
|
||||
const expandedPrefixDirectory = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + getDirectoryPath(fragment)) : normalizedPrefixDirectory;
|
||||
const expandedPrefixDirectory = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + fragmentDirectory) : normalizedPrefixDirectory;
|
||||
|
||||
const normalizedSuffix = normalizePath(parsed.suffix);
|
||||
// Need to normalize after combining: If we combinePaths("a", "../b"), we want "b" and not "a/../b".
|
||||
@ -418,16 +422,6 @@ namespace ts.Completions.PathCompletions {
|
||||
return false;
|
||||
}
|
||||
|
||||
function normalizeAndPreserveTrailingSlash(path: string) {
|
||||
if (normalizeSlashes(path) === "./") {
|
||||
// normalizePath turns "./" into "". "" + "/" would then be a rooted path instead of a relative one, so avoid this particular case.
|
||||
// There is no problem for adding "/" to a non-empty string -- it's only a problem at the beginning.
|
||||
return "";
|
||||
}
|
||||
const norm = normalizePath(path);
|
||||
return hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(norm) : norm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a triple slash reference directive with an incomplete string literal for its path. Used
|
||||
* to determine if the caret is currently within the string literal and capture the literal fragment
|
||||
|
||||
@ -38,6 +38,6 @@ _2["default"].aIndex;
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var __1 = require("..");
|
||||
var _1 = require("../");
|
||||
var __2 = require("../");
|
||||
__1["default"].a;
|
||||
_1["default"].aIndex;
|
||||
__2["default"].aIndex;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user