diff --git a/src/harness/core/collections.ts b/src/harness/collections.ts
similarity index 55%
rename from src/harness/core/collections.ts
rename to src/harness/collections.ts
index 7936b5a9323..7c71296409c 100644
--- a/src/harness/core/collections.ts
+++ b/src/harness/collections.ts
@@ -1,5 +1,4 @@
-///
-namespace core {
+namespace collections {
export interface SortOptions {
comparer: (a: T, b: T) => number;
sort: "insertion" | "comparison";
@@ -43,16 +42,16 @@ namespace core {
}
public has(key: K) {
- return binarySearch(this._keys, key, identity, this._comparer) >= 0;
+ return ts.binarySearch(this._keys, key, ts.identity, this._comparer) >= 0;
}
public get(key: K) {
- const index = binarySearch(this._keys, key, identity, this._comparer);
+ const index = ts.binarySearch(this._keys, key, ts.identity, this._comparer);
return index >= 0 ? this._values[index] : undefined;
}
public set(key: K, value: V) {
- const index = binarySearch(this._keys, key, identity, this._comparer);
+ const index = ts.binarySearch(this._keys, key, ts.identity, this._comparer);
if (index >= 0) {
this._values[index] = value;
}
@@ -67,12 +66,12 @@ namespace core {
}
public delete(key: K) {
- const index = binarySearch(this._keys, key, identity, this._comparer);
+ const index = ts.binarySearch(this._keys, key, ts.identity, this._comparer);
if (index >= 0) {
this.writePreamble();
- removeAt(this._keys, index);
- removeAt(this._values, index);
- if (this._order) removeAt(this._order, index);
+ ts.orderedRemoveItemAt(this._keys, index);
+ ts.orderedRemoveItemAt(this._values, index);
+ if (this._order) ts.orderedRemoveItemAt(this._order, index);
this.writePostScript();
return true;
}
@@ -211,226 +210,6 @@ namespace core {
}
}
- export class SortedSet {
- private _comparer: (a: T, b: T) => number;
- private _values: T[] = [];
- private _order: number[] | undefined;
- private _version = 0;
- private _copyOnWrite = false;
-
- constructor(comparer: ((a: T, b: T) => number) | SortOptions, iterable?: Iterable) {
- this._comparer = typeof comparer === "object" ? comparer.comparer : comparer;
- this._order = typeof comparer === "object" && comparer.sort === "insertion" ? [] : undefined;
-
- if (iterable) {
- const iterator = getIterator(iterable);
- try {
- for (let i = nextResult(iterator); i; i = nextResult(iterator)) {
- const value = i.value;
- this.add(value);
- }
- }
- finally {
- closeIterator(iterator);
- }
- }
- }
-
- public get size() {
- return this._values.length;
- }
-
- public get comparer() {
- return this._comparer;
- }
-
- public get [Symbol.toStringTag]() {
- return "SortedSet";
- }
-
- public has(value: T) {
- return binarySearch(this._values, value, identity, this._comparer) >= 0;
- }
-
- public add(value: T) {
- const index = binarySearch(this._values, value, identity, this._comparer);
- if (index < 0) {
- this.writePreamble();
- insertAt(this._values, ~index, value);
- if (this._order) insertAt(this._order, ~index, this._version);
- this.writePostScript();
- }
- return this;
- }
-
- public delete(value: T) {
- const index = binarySearch(this._values, value, identity, this._comparer);
- if (index >= 0) {
- this.writePreamble();
- removeAt(this._values, index);
- if (this._order) removeAt(this._order, index);
- this.writePostScript();
- return true;
- }
- return false;
- }
-
- public clear() {
- if (this.size > 0) {
- this.writePreamble();
- this._values.length = 0;
- if (this._order) this._order.length = 0;
- this.writePostScript();
- }
- }
-
- public forEach(callback: (value: T, key: T, collection: this) => void, thisArg?: any) {
- const values = this._values;
- const indices = this.getIterationOrder();
- const version = this._version;
- this._copyOnWrite = true;
- try {
- if (indices) {
- for (const i of indices) {
- callback.call(thisArg, values[i], values[i], this);
- }
- }
- else {
- for (const value of values) {
- callback.call(thisArg, value, value, this);
- }
- }
- }
- finally {
- if (version === this._version) {
- this._copyOnWrite = false;
- }
- }
- }
-
- public keys() {
- return this.values();
- }
-
- public * values() {
- const values = this._values;
- const indices = this.getIterationOrder();
- const version = this._version;
- this._copyOnWrite = true;
- try {
- if (indices) {
- for (const i of indices) {
- yield values[i];
- }
- }
- else {
- for (const value of values) {
- yield value;
- }
- }
- }
- finally {
- if (version === this._version) {
- this._copyOnWrite = false;
- }
- }
- }
-
- public * entries() {
- const values = this._values;
- const indices = this.getIterationOrder();
- const version = this._version;
- this._copyOnWrite = true;
- try {
- if (indices) {
- for (const i of indices) {
- yield [values[i], values[i]] as [T, T];
- }
- }
- else {
- for (const value of values) {
- yield [value, value] as [T, T];
- }
- }
- }
- finally {
- if (version === this._version) {
- this._copyOnWrite = false;
- }
- }
- }
-
- public [Symbol.iterator]() {
- return this.values();
- }
-
- private writePreamble() {
- if (this._copyOnWrite) {
- this._values = this._values.slice();
- if (this._order) this._order = this._order.slice();
- this._copyOnWrite = false;
- }
- }
-
- private writePostScript() {
- this._version++;
- }
-
- private getIterationOrder() {
- if (this._order) {
- const order = this._order;
- return this._order
- .map((_, i) => i)
- .sort((x, y) => order[x] - order[y]);
- }
- return undefined;
- }
- }
-
- export function binarySearch(array: ReadonlyArray, value: T, keySelector: (v: T) => U, keyComparer: (a: U, b: U) => number, offset?: number): number {
- if (!array || array.length === 0) {
- return -1;
- }
-
- let low = offset || 0;
- let high = array.length - 1;
- const key = keySelector(value);
- while (low <= high) {
- const middle = low + ((high - low) >> 1);
- const midKey = keySelector(array[middle]);
- const result = keyComparer(midKey, key);
- if (result < 0) {
- low = middle + 1;
- }
- else if (result > 0) {
- high = middle - 1;
- }
- else {
- return middle;
- }
- }
-
- return ~low;
- }
-
- export function removeAt(array: T[], index: number): void {
- if (index < 0 || index >= array.length) {
- return;
- }
- else if (index === 0) {
- array.shift();
- }
- else if (index === array.length - 1) {
- array.pop();
- }
- else {
- for (let i = index; i < array.length - 1; i++) {
- array[i] = array[i + 1];
- }
- array.length--;
- }
- }
-
export function insertAt(array: T[], index: number, value: T): void {
if (index === 0) {
array.unshift(value);
diff --git a/src/harness/compiler.ts b/src/harness/compiler.ts
index f9df4167276..8308074c444 100644
--- a/src/harness/compiler.ts
+++ b/src/harness/compiler.ts
@@ -1,14 +1,6 @@
-///
-///
-///
-///
-///
-///
-///
-
-// NOTE: The contents of this file are all exported from the namespace 'compiler'. This is to
-// support the eventual conversion of harness into a modular system.
-
+/**
+ * Test harness compiler functionality.
+ */
namespace compiler {
export interface Project {
file: string;
@@ -64,7 +56,7 @@ namespace compiler {
public readonly maps: ReadonlyMap;
private _inputs: documents.TextDocument[] = [];
- private _inputsAndOutputs: core.SortedMap;
+ private _inputsAndOutputs: collections.SortedMap;
constructor(host: fakes.CompilerHost, options: ts.CompilerOptions, program: ts.Program | undefined, result: ts.EmitResult | undefined, diagnostics: ts.Diagnostic[]) {
this.host = host;
@@ -74,9 +66,9 @@ namespace compiler {
this.options = program ? program.getCompilerOptions() : options;
// collect outputs
- const js = this.js = new core.SortedMap({ comparer: this.vfs.stringComparer, sort: "insertion" });
- const dts = this.dts = new core.SortedMap({ comparer: this.vfs.stringComparer, sort: "insertion" });
- const maps = this.maps = new core.SortedMap({ comparer: this.vfs.stringComparer, sort: "insertion" });
+ const js = this.js = new collections.SortedMap({ comparer: this.vfs.stringComparer, sort: "insertion" });
+ const dts = this.dts = new collections.SortedMap({ comparer: this.vfs.stringComparer, sort: "insertion" });
+ const maps = this.maps = new collections.SortedMap({ comparer: this.vfs.stringComparer, sort: "insertion" });
for (const document of this.host.outputs) {
if (vpath.isJavaScript(document.file)) {
js.set(document.file, document);
@@ -90,7 +82,7 @@ namespace compiler {
}
// correlate inputs and outputs
- this._inputsAndOutputs = new core.SortedMap({ comparer: this.vfs.stringComparer, sort: "insertion" });
+ this._inputsAndOutputs = new collections.SortedMap({ comparer: this.vfs.stringComparer, sort: "insertion" });
if (program) {
if (this.options.out || this.options.outFile) {
const outFile = vpath.resolve(this.vfs.cwd(), this.options.outFile || this.options.out);
diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts
index 8ccf756a9d7..1ffa2fc63a7 100644
--- a/src/harness/compilerRunner.ts
+++ b/src/harness/compilerRunner.ts
@@ -273,8 +273,8 @@ class CompilerTest {
}
private makeUnitName(name: string, root: string) {
- const path = ts.toPath(name, root, core.identity);
- const pathStart = ts.toPath(Harness.IO.getCurrentDirectory(), "", core.identity);
+ const path = ts.toPath(name, root, ts.identity);
+ const pathStart = ts.toPath(Harness.IO.getCurrentDirectory(), "", ts.identity);
return pathStart ? path.replace(pathStart, "/") : path;
}
diff --git a/src/harness/core.ts b/src/harness/core.ts
deleted file mode 100644
index cb94af50bde..00000000000
--- a/src/harness/core.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-///
-///
-///
-///
\ No newline at end of file
diff --git a/src/harness/core/comparers.ts b/src/harness/core/comparers.ts
deleted file mode 100644
index c0846437308..00000000000
--- a/src/harness/core/comparers.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-namespace core {
- export function compareNumbers(a: number, b: number): number {
- if (a === b) return 0;
- if (a === undefined) return -1;
- if (b === undefined) return +1;
- return a < b ? -1 : +1;
- }
-
- export function compareStrings(a: string, b: string, ignoreCase: boolean): number {
- return ignoreCase
- ? compareStringsCaseInsensitive(a, b)
- : compareStringsCaseSensitive(a, b);
- }
-
- // NOTE: This is a duplicate of `compareNumbers` above, but is intended to be used only with
- // strings to reduce polymorphism.
- export function compareStringsCaseSensitive(a: string, b: string): number {
- if (a === b) return 0;
- if (a === undefined) return -1;
- if (b === undefined) return +1;
- return a < b ? -1 : +1;
- }
-
- export function compareStringsCaseInsensitive(a: string, b: string): number {
- if (a === b) return 0;
- if (a === undefined) return -1;
- if (b === undefined) return +1;
- a = a.toUpperCase();
- b = b.toUpperCase();
- return a < b ? -1 : a > b ? +1 : 0;
- }
-
- export function equateStringsCaseSensitive(a: string, b: string): boolean {
- return a === b;
- }
-
- export function equateStringsCaseInsensitive(a: string, b: string): boolean {
- return a === b
- || a !== undefined
- && b !== undefined
- && a.toUpperCase() === b.toUpperCase();
- }
-}
\ No newline at end of file
diff --git a/src/harness/core/functions.ts b/src/harness/core/functions.ts
deleted file mode 100644
index 870ea844b32..00000000000
--- a/src/harness/core/functions.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace core {
- export function identity(v: T): T { return v; }
-}
diff --git a/src/harness/core/strings.ts b/src/harness/core/strings.ts
deleted file mode 100644
index 593ef23c83f..00000000000
--- a/src/harness/core/strings.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-namespace core {
- export function padLeft(text: string, size: number, ch = " "): string {
- while (text.length < size) text = ch + text;
- return text;
- }
-
- export function padRight(text: string, size: number, ch = " "): string {
- while (text.length < size) text += ch;
- return text;
- }
-
- export function getByteOrderMark(text: string): string {
- const length = getByteOrderMarkLength(text);
- return length > 0 ? text.slice(0, length) : "";
- }
-
- export function getByteOrderMarkLength(text: string): number {
- if (text.length >= 1) {
- const ch0 = text.charCodeAt(0);
- if (ch0 === 0xfeff) return 1;
- if (ch0 === 0xfe) return text.length >= 2 && text.charCodeAt(1) === 0xff ? 2 : 0;
- if (ch0 === 0xff) return text.length >= 2 && text.charCodeAt(1) === 0xfe ? 2 : 0;
- if (ch0 === 0xef) return text.length >= 3 && text.charCodeAt(1) === 0xbb && text.charCodeAt(2) === 0xbf ? 3 : 0;
- }
- return 0;
- }
-
- export function removeByteOrderMark(text: string): string {
- const length = getByteOrderMarkLength(text);
- return length ? text.slice(length) : text;
- }
-
- export function addUTF8ByteOrderMark(text: string) {
- return getByteOrderMarkLength(text) === 0 ? "\u00EF\u00BB\u00BF" + text : text;
- }
-
- function splitLinesWorker(text: string, lineStarts: number[] | undefined, lines: string[] | undefined, removeEmptyElements: boolean) {
- let pos = 0;
- let end = 0;
- let lineStart = 0;
- let nonWhiteSpace = false;
- while (pos < text.length) {
- const ch = text.charCodeAt(pos);
- end = pos;
- pos++;
- switch (ch) {
- // LineTerminator
- case 0x000d: // carriage return
- if (pos < text.length && text.charCodeAt(pos) === 0x000a) {
- pos++;
- }
- // falls through
-
- case 0x000a: // line feed
- case 0x2028: // line separator
- case 0x2029: // paragraph separator
- if (lineStarts) {
- lineStarts.push(lineStart);
- }
- if (lines && (!removeEmptyElements || nonWhiteSpace)) {
- lines.push(text.slice(lineStart, end));
- }
- lineStart = pos;
- nonWhiteSpace = false;
- break;
-
- // WhiteSpace
- case 0x0009: // tab
- case 0x000b: // vertical tab
- case 0x000c: // form feed
- case 0x0020: // space
- case 0x00a0: // no-break space
- case 0xfeff: // zero width no-break space
- case 0x1680: // ogham space mark
- case 0x2000: // en quad
- case 0x2001: // em quad
- case 0x2002: // en space
- case 0x2003: // em space
- case 0x2004: // three-per-em space
- case 0x2005: // four-per-em space
- case 0x2006: // six-per-em space
- case 0x2007: // figure space
- case 0x2008: // punctuation space
- case 0x2009: // thin space
- case 0x200a: // hair space
- case 0x202f: // narrow no-break space
- case 0x205f: // medium mathematical space
- case 0x3000: // ideographic space
- case 0x0085: // next-line (not strictly per spec, but used by the compiler)
- break;
-
- default:
- nonWhiteSpace = true;
- break;
- }
- }
- if (lineStarts) {
- lineStarts.push(lineStart);
- }
- if (lines && (!removeEmptyElements || nonWhiteSpace)) {
- lines.push(text.slice(lineStart, text.length));
- }
- }
-
- export type LineStarts = ReadonlyArray;
-
- export interface LinesAndLineStarts {
- readonly lines: ReadonlyArray;
- readonly lineStarts: LineStarts;
- }
-
- export function getLinesAndLineStarts(text: string): LinesAndLineStarts {
- const lines: string[] = [];
- const lineStarts: number[] = [];
- splitLinesWorker(text, lineStarts, lines, /*removeEmptyElements*/ false);
- return { lines, lineStarts };
- }
-
- export function splitLines(text: string, removeEmptyElements = false): string[] {
- const lines: string[] = [];
- splitLinesWorker(text, /*lineStarts*/ undefined, lines, removeEmptyElements);
- return lines;
- }
-
- export function computeLineStarts(text: string): LineStarts {
- const lineStarts: number[] = [];
- splitLinesWorker(text, lineStarts, /*lines*/ undefined, /*removeEmptyElements*/ false);
- return lineStarts;
- }
-}
\ No newline at end of file
diff --git a/src/harness/documents.ts b/src/harness/documents.ts
index fc4893ef4e8..f5219029109 100644
--- a/src/harness/documents.ts
+++ b/src/harness/documents.ts
@@ -1,5 +1,3 @@
-///
-
// NOTE: The contents of this file are all exported from the namespace 'documents'. This is to
// support the eventual conversion of harness into a modular system.
@@ -9,7 +7,7 @@ namespace documents {
public readonly file: string;
public readonly text: string;
- private _lineStarts: core.LineStarts | undefined;
+ private _lineStarts: ReadonlyArray | undefined;
private _testFile: Harness.Compiler.TestFile | undefined;
constructor(file: string, text: string, meta?: Map) {
@@ -18,8 +16,8 @@ namespace documents {
this.meta = meta || new Map();
}
- public get lineStarts(): core.LineStarts {
- return this._lineStarts || (this._lineStarts = core.computeLineStarts(this.text));
+ public get lineStarts(): ReadonlyArray {
+ return this._lineStarts || (this._lineStarts = ts.computeLineStarts(this.text));
}
public static fromTestFile(file: Harness.Compiler.TestFile) {
diff --git a/src/harness/fakes.ts b/src/harness/fakes.ts
index 9b1b83666cf..669cb33c0c4 100644
--- a/src/harness/fakes.ts
+++ b/src/harness/fakes.ts
@@ -1,11 +1,6 @@
-///
-///
-///
-
-// NOTE: The contents of this file are all exported from the namespace 'fakes'. This is to
-// support the eventual conversion of harness into a modular system.
-
-// harness fakes
+/**
+ * Fake implementations of various compiler dependencies.
+ */
namespace fakes {
const processExitSentinel = new Error("System exit");
@@ -45,8 +40,8 @@ namespace fakes {
try {
const content = this.vfs.readFileSync(path, "utf8");
return content === undefined ? undefined :
- vpath.extname(path) === ".json" ? utils.removeComments(core.removeByteOrderMark(content), utils.CommentRemoval.leadingAndTrailing) :
- core.removeByteOrderMark(content);
+ vpath.extname(path) === ".json" ? utils.removeComments(utils.removeByteOrderMark(content), utils.CommentRemoval.leadingAndTrailing) :
+ utils.removeByteOrderMark(content);
}
catch {
return undefined;
@@ -55,7 +50,7 @@ namespace fakes {
public writeFile(path: string, data: string, writeByteOrderMark?: boolean): void {
this.vfs.mkdirpSync(vpath.dirname(path));
- this.vfs.writeFileSync(path, writeByteOrderMark ? core.addUTF8ByteOrderMark(data) : data);
+ this.vfs.writeFileSync(path, writeByteOrderMark ? utils.addUTF8ByteOrderMark(data) : data);
}
public fileExists(path: string) {
@@ -212,7 +207,7 @@ namespace fakes {
public readonly shouldAssertInvariants = !Harness.lightMode;
private _setParentNodes: boolean;
- private _sourceFiles: core.SortedMap;
+ private _sourceFiles: collections.SortedMap;
private _parseConfigHost: ParseConfigHost;
private _newLine: string;
@@ -221,7 +216,7 @@ namespace fakes {
this.sys = sys;
this.defaultLibLocation = sys.vfs.meta.get("defaultLibLocation") || "";
this._newLine = ts.getNewLineCharacter(options, () => this.sys.newLine);
- this._sourceFiles = new core.SortedMap({ comparer: sys.vfs.stringComparer, sort: "insertion" });
+ this._sourceFiles = new collections.SortedMap({ comparer: sys.vfs.stringComparer, sort: "insertion" });
this._setParentNodes = setParentNodes;
}
@@ -266,7 +261,7 @@ namespace fakes {
}
public writeFile(fileName: string, content: string, writeByteOrderMark: boolean) {
- if (writeByteOrderMark) content = core.addUTF8ByteOrderMark(content);
+ if (writeByteOrderMark) content = utils.addUTF8ByteOrderMark(content);
this.sys.writeFile(fileName, content);
const document = new documents.TextDocument(fileName, content);
diff --git a/src/harness/harness.ts b/src/harness/harness.ts
index d7db8eb8cfe..2a477b07399 100644
--- a/src/harness/harness.ts
+++ b/src/harness/harness.ts
@@ -678,9 +678,9 @@ namespace Harness {
function createBrowserIO(): IO {
const serverRoot = new URL("http://localhost:8888/");
- class HttpHeaders extends core.SortedMap {
+ class HttpHeaders extends collections.SortedMap {
constructor(template?: Record) {
- super(core.compareStringsCaseInsensitive);
+ super(ts.compareStringsCaseInsensitive);
if (template) {
for (const key in template) {
if (ts.hasProperty(template, key)) {
@@ -1237,7 +1237,7 @@ namespace Harness {
}
const docs = inputFiles.concat(otherFiles).map(documents.TextDocument.fromTestFile);
- const fs = vfs.FileSystem.createFromFileSystem(IO, !useCaseSensitiveFileNames, { documents: docs, cwd: currentDirectory });
+ const fs = vfs.createFromFileSystem(IO, !useCaseSensitiveFileNames, { documents: docs, cwd: currentDirectory });
const host = new fakes.CompilerHost(fs, options);
return compiler.compileFiles(host, programFileNames, options);
}
@@ -1286,7 +1286,7 @@ namespace Harness {
else if (vpath.isTypeScript(file.unitName)) {
const declFile = findResultCodeFile(file.unitName);
if (declFile && !findUnit(declFile.file, declInputFiles) && !findUnit(declFile.file, declOtherFiles)) {
- dtsFiles.push({ unitName: declFile.file, content: core.removeByteOrderMark(declFile.text) });
+ dtsFiles.push({ unitName: declFile.file, content: utils.removeByteOrderMark(declFile.text) });
}
}
}
@@ -1733,7 +1733,7 @@ namespace Harness {
const dupeCase = ts.createMap();
// Yield them
for (const outputFile of files) {
- yield [checkDuplicatedFileName(outputFile.file, dupeCase), "/*====== " + outputFile.file + " ======*/\r\n" + core.removeByteOrderMark(outputFile.text)];
+ yield [checkDuplicatedFileName(outputFile.file, dupeCase), "/*====== " + outputFile.file + " ======*/\r\n" + utils.removeByteOrderMark(outputFile.text)];
}
function cleanName(fn: string) {
diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts
index 9f1587b85b5..34d6f775a74 100644
--- a/src/harness/harnessLanguageService.ts
+++ b/src/harness/harnessLanguageService.ts
@@ -119,11 +119,11 @@ namespace Harness.LanguageService {
export abstract class LanguageServiceAdapterHost {
public readonly sys = new fakes.System(new vfs.FileSystem(/*ignoreCase*/ true, { cwd: virtualFileSystemRoot }));
public typesRegistry: ts.Map | undefined;
- private scriptInfos: core.SortedMap;
+ private scriptInfos: collections.SortedMap;
constructor(protected cancellationToken = DefaultHostCancellationToken.instance,
protected settings = ts.getDefaultCompilerOptions()) {
- this.scriptInfos = new core.SortedMap({ comparer: this.vfs.stringComparer, sort: "insertion" });
+ this.scriptInfos = new collections.SortedMap({ comparer: this.vfs.stringComparer, sort: "insertion" });
}
public get vfs() {
diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts
index e7d9b387b46..3aa18b1ba27 100644
--- a/src/harness/projectsRunner.ts
+++ b/src/harness/projectsRunner.ts
@@ -193,7 +193,7 @@ namespace project {
assert(false, "Testcase: " + testCaseFileName + " does not contain valid json format: " + e.message);
}
- const fs = vfs.FileSystem.createFromFileSystem(Harness.IO, /*ignoreCase*/ false);
+ const fs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false);
fs.mountSync(vpath.resolve(Harness.IO.getWorkspaceRoot(), "tests"), vpath.combine(vfs.srcFolder, "tests"), vfs.createResolver(Harness.IO));
fs.mkdirpSync(vpath.combine(vfs.srcFolder, testCase.projectRoot));
fs.chdir(vpath.combine(vfs.srcFolder, testCase.projectRoot));
@@ -393,7 +393,7 @@ namespace project {
}
});
- const _vfs = vfs.FileSystem.createFromFileSystem(Harness.IO, /*ignoreCase*/ false, {
+ const _vfs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false, {
documents: allInputFiles,
cwd: vpath.combine(vfs.srcFolder, this.testCase.projectRoot)
});
diff --git a/src/harness/sourceMapRecorder.ts b/src/harness/sourceMapRecorder.ts
index 68ac32a5687..1e13e3833a4 100644
--- a/src/harness/sourceMapRecorder.ts
+++ b/src/harness/sourceMapRecorder.ts
@@ -317,7 +317,7 @@ namespace Harness.SourceMapRecorder {
const startPos = lineMap[line];
const endPos = lineMap[line + 1];
const text = code.substring(startPos, endPos);
- return line === 0 ? core.removeByteOrderMark(text) : text;
+ return line === 0 ? utils.removeByteOrderMark(text) : text;
}
function writeJsFileLines(endJsLine: number) {
diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json
index e9674d7e452..67eeeb14f37 100644
--- a/src/harness/tsconfig.json
+++ b/src/harness/tsconfig.json
@@ -134,12 +134,7 @@
"../server/session.ts",
"../server/scriptVersionCache.ts",
- "./core/functions.ts",
- "./core/comparers.ts",
- "./core/collections.ts",
- "./core/strings.ts",
- "core.ts",
-
+ "collections.ts",
"utils.ts",
"documents.ts",
"vpath.ts",
diff --git a/src/harness/unittests/programMissingFiles.ts b/src/harness/unittests/programMissingFiles.ts
index efbf42e6004..66b2ab4cf7f 100644
--- a/src/harness/unittests/programMissingFiles.ts
+++ b/src/harness/unittests/programMissingFiles.ts
@@ -37,7 +37,7 @@ namespace ts {
);
const testCompilerHost = new fakes.CompilerHost(
- vfs.FileSystem.createFromFileSystem(
+ vfs.createFromFileSystem(
Harness.IO,
/*ignoreCase*/ true,
{ documents: [emptyFile, referenceFile], cwd: "d:\\pretend\\" }),
diff --git a/src/harness/unittests/publicApi.ts b/src/harness/unittests/publicApi.ts
index e1a0b6ef673..b99557b67bd 100644
--- a/src/harness/unittests/publicApi.ts
+++ b/src/harness/unittests/publicApi.ts
@@ -14,7 +14,7 @@ describe("Public APIs", () => {
});
it("should compile", () => {
- const fs = vfs.FileSystem.createFromFileSystem(Harness.IO, /*ignoreCase*/ false);
+ const fs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false);
fs.linkSync(`${vfs.builtFolder}/${fileName}`, `${vfs.srcFolder}/${fileName}`);
const sys = new fakes.System(fs);
const host = new fakes.CompilerHost(sys);
diff --git a/src/harness/utils.ts b/src/harness/utils.ts
index 2e53c3a5cb0..81da505cd90 100644
--- a/src/harness/utils.ts
+++ b/src/harness/utils.ts
@@ -1,8 +1,6 @@
-///
-
-// NOTE: The contents of this file are all exported from the namespace 'utils'. This is to
-// support the eventual conversion of harness into a modular system.
-
+/**
+ * Common utilities
+ */
namespace utils {
const leadingCommentRegExp = /^(\s*\/\*[^]*?\*\/\s*|\s*\/\/[^\r\n\u2028\u2029]*[\r\n\u2028\u2029]*)+/;
const trailingCommentRegExp = /(\s*\/\*[^]*?\*\/\s*|\s*\/\/[^\r\n\u2028\u2029]*[\r\n\u2028\u2029]*)+$/;
@@ -34,28 +32,6 @@ namespace utils {
return text !== undefined ? text.replace(testPathPrefixRegExp, "") : undefined;
}
- /**
- * SameValueZero (from ECMAScript spec), which has stricter equality sematics than "==" or "===".
- */
- export function is(x: any, y: any) {
- return (x === y) ? (x !== 0 || 1 / x === 1 / y) : (x !== x && y !== y);
- }
-
- export interface Theory {
- title: string;
- args: any[];
- }
-
- export function theory(name: string, data: (Theory | any[])[], callback: (...args: any[]) => any) {
- describe(name, () => {
- for (const theory of data) {
- const title = Array.isArray(theory) ? theory.toString() : theory.title;
- const args = Array.isArray(theory) ? theory : theory.args;
- it(title, () => callback(...args));
- }
- });
- }
-
/**
* Removes leading indentation from a template literal string.
*/
@@ -112,14 +88,27 @@ namespace utils {
return indentation;
}
- export function checkFileNames(caption: string, actualFileNames: ReadonlyArray, expectedFileNames: string[]) {
- assert.equal(actualFileNames.length, expectedFileNames.length, `${caption}: incorrect actual number of files, expected ${expectedFileNames}, got ${actualFileNames}`);
- for (const f of expectedFileNames) {
- assert.isTrue(actualFileNames.indexOf(f) >= 0, `${caption}: expected to find ${f} in ${actualFileNames}`);
- }
- }
-
export function toUtf8(text: string): string {
return new Buffer(text).toString("utf8");
}
+
+ export function getByteOrderMarkLength(text: string): number {
+ if (text.length >= 1) {
+ const ch0 = text.charCodeAt(0);
+ if (ch0 === 0xfeff) return 1;
+ if (ch0 === 0xfe) return text.length >= 2 && text.charCodeAt(1) === 0xff ? 2 : 0;
+ if (ch0 === 0xff) return text.length >= 2 && text.charCodeAt(1) === 0xfe ? 2 : 0;
+ if (ch0 === 0xef) return text.length >= 3 && text.charCodeAt(1) === 0xbb && text.charCodeAt(2) === 0xbf ? 3 : 0;
+ }
+ return 0;
+ }
+
+ export function removeByteOrderMark(text: string): string {
+ const length = getByteOrderMarkLength(text);
+ return length ? text.slice(length) : text;
+ }
+
+ export function addUTF8ByteOrderMark(text: string) {
+ return getByteOrderMarkLength(text) === 0 ? "\u00EF\u00BB\u00BF" + text : text;
+ }
}
\ No newline at end of file
diff --git a/src/harness/vfs.ts b/src/harness/vfs.ts
index 49c72bfde58..cbd3f5a11d2 100644
--- a/src/harness/vfs.ts
+++ b/src/harness/vfs.ts
@@ -40,9 +40,9 @@ namespace vfs {
// lazy-initialized state that should be mutable even if the FileSystem is frozen.
private _lazy: {
- links?: core.SortedMap;
+ links?: collections.SortedMap;
shadows?: Map;
- meta?: core.Metadata;
+ meta?: collections.Metadata;
} = {};
private _cwd: string; // current working directory
@@ -68,16 +68,16 @@ namespace vfs {
let cwd = options.cwd;
if ((!cwd || !vpath.isRoot(cwd)) && this._lazy.links) {
- const iterator = core.getIterator(this._lazy.links.keys());
+ const iterator = collections.getIterator(this._lazy.links.keys());
try {
- for (let i = core.nextResult(iterator); i; i = core.nextResult(iterator)) {
+ for (let i = collections.nextResult(iterator); i; i = collections.nextResult(iterator)) {
const name = i.value;
cwd = cwd ? vpath.resolve(name, cwd) : name;
break;
}
}
finally {
- core.closeIterator(iterator);
+ collections.closeIterator(iterator);
}
}
@@ -92,9 +92,9 @@ namespace vfs {
/**
* Gets metadata for this `FileSystem`.
*/
- public get meta(): core.Metadata {
+ public get meta(): collections.Metadata {
if (!this._lazy.meta) {
- this._lazy.meta = new core.Metadata(this._shadowRoot ? this._shadowRoot.meta : undefined);
+ this._lazy.meta = new collections.Metadata(this._shadowRoot ? this._shadowRoot.meta : undefined);
}
return this._lazy.meta;
}
@@ -121,40 +121,6 @@ namespace vfs {
return this._shadowRoot;
}
- /**
- * Create a virtual file system from a physical file system using the following path mappings:
- *
- * - `/.ts` is a directory mapped to `${workspaceRoot}/built/local`
- * - `/.lib` is a directory mapped to `${workspaceRoot}/tests/lib`
- * - `/.src` is a virtual directory to be used for tests.
- *
- * Unless overridden, `/.src` will be the current working directory for the virtual file system.
- */
- public static createFromFileSystem(host: FileSystemResolverHost, ignoreCase: boolean, { documents, cwd }: FileSystemCreateOptions = {}) {
- const fs = getBuiltLocal(host, ignoreCase).shadow();
- if (cwd) {
- fs.mkdirpSync(cwd);
- fs.chdir(cwd);
- }
- if (documents) {
- for (const document of documents) {
- fs.mkdirpSync(vpath.dirname(document.file));
- fs.writeFileSync(document.file, document.text, "utf8");
- fs.filemeta(document.file).set("document", document);
- // Add symlinks
- const symlink = document.meta.get("symlink");
- if (symlink) {
- for (const link of symlink.split(",").map(link => link.trim())) {
- fs.mkdirpSync(vpath.dirname(link));
- fs.symlinkSync(document.file, link);
- fs.filemeta(link).set("document", document);
- }
- }
- }
- }
- return fs;
- }
-
/**
* Gets a shadow copy of this file system. Changes to the shadow copy do not affect the
* original, allowing multiple copies of the same core file system without multiple copies
@@ -190,16 +156,16 @@ namespace vfs {
* Gets the metadata object for a path.
* @param path
*/
- public filemeta(path: string): core.Metadata {
+ public filemeta(path: string): collections.Metadata {
const { node } = this._walk(this._resolve(path));
if (!node) throw createIOError("ENOENT");
return this._filemeta(node);
}
- private _filemeta(node: Inode): core.Metadata {
+ private _filemeta(node: Inode): collections.Metadata {
if (!node.meta) {
const parentMeta = node.shadowRoot && this._shadowRoot && this._shadowRoot._filemeta(node.shadowRoot);
- node.meta = new core.Metadata(parentMeta);
+ node.meta = new collections.Metadata(parentMeta);
}
return node.meta;
}
@@ -390,10 +356,10 @@ namespace vfs {
*/
public debugPrint(): void {
let result = "";
- const printLinks = (dirname: string | undefined, links: core.SortedMap) => {
- const iterator = core.getIterator(links);
+ const printLinks = (dirname: string | undefined, links: collections.SortedMap) => {
+ const iterator = collections.getIterator(links);
try {
- for (let i = core.nextResult(iterator); i; i = core.nextResult(iterator)) {
+ for (let i = collections.nextResult(iterator); i; i = collections.nextResult(iterator)) {
const [name, node] = i.value;
const path = dirname ? vpath.combine(dirname, name) : name;
const marker = vpath.compare(this._cwd, path, this.ignoreCase) === 0 ? "*" : " ";
@@ -412,7 +378,7 @@ namespace vfs {
}
}
finally {
- core.closeIterator(iterator);
+ collections.closeIterator(iterator);
}
};
printLinks(/*dirname*/ undefined, this._getRootLinks());
@@ -686,7 +652,7 @@ namespace vfs {
};
}
- private _addLink(parent: DirectoryInode | undefined, links: core.SortedMap, name: string, node: Inode, time = this.time()) {
+ private _addLink(parent: DirectoryInode | undefined, links: collections.SortedMap, name: string, node: Inode, time = this.time()) {
links.set(name, node);
node.nlink++;
node.ctimeMs = time;
@@ -694,14 +660,14 @@ namespace vfs {
if (!parent && !this._cwd) this._cwd = name;
}
- private _removeLink(parent: DirectoryInode | undefined, links: core.SortedMap, name: string, node: Inode, time = this.time()) {
+ private _removeLink(parent: DirectoryInode | undefined, links: collections.SortedMap, name: string, node: Inode, time = this.time()) {
links.delete(name);
node.nlink--;
node.ctimeMs = time;
if (parent) parent.mtimeMs = time;
}
- private _replaceLink(oldParent: DirectoryInode, oldLinks: core.SortedMap, oldName: string, newParent: DirectoryInode, newLinks: core.SortedMap, newName: string, node: Inode, time: number) {
+ private _replaceLink(oldParent: DirectoryInode, oldLinks: collections.SortedMap, oldName: string, newParent: DirectoryInode, newLinks: collections.SortedMap, newName: string, node: Inode, time: number) {
if (oldParent !== newParent) {
this._removeLink(oldParent, oldLinks, oldName, node, time);
this._addLink(newParent, newLinks, newName, node, time);
@@ -716,7 +682,7 @@ namespace vfs {
private _getRootLinks() {
if (!this._lazy.links) {
- this._lazy.links = new core.SortedMap(this.stringComparer);
+ this._lazy.links = new collections.SortedMap(this.stringComparer);
if (this._shadowRoot) {
this._copyShadowLinks(this._shadowRoot._getRootLinks(), this._lazy.links);
}
@@ -727,7 +693,7 @@ namespace vfs {
private _getLinks(node: DirectoryInode) {
if (!node.links) {
- const links = new core.SortedMap(this.stringComparer);
+ const links = new collections.SortedMap(this.stringComparer);
const { source, resolver } = node;
if (source && resolver) {
node.source = undefined;
@@ -786,16 +752,16 @@ namespace vfs {
return shadow;
}
- private _copyShadowLinks(source: ReadonlyMap, target: core.SortedMap) {
- const iterator = core.getIterator(source);
+ private _copyShadowLinks(source: ReadonlyMap, target: collections.SortedMap) {
+ const iterator = collections.getIterator(source);
try {
- for (let i = core.nextResult(iterator); i; i = core.nextResult(iterator)) {
+ for (let i = collections.nextResult(iterator); i; i = collections.nextResult(iterator)) {
const [name, root] = i.value;
target.set(name, this._getShadow(root));
}
}
finally {
- core.closeIterator(iterator);
+ collections.closeIterator(iterator);
}
}
@@ -1035,6 +1001,40 @@ namespace vfs {
};
}
+ /**
+ * Create a virtual file system from a physical file system using the following path mappings:
+ *
+ * - `/.ts` is a directory mapped to `${workspaceRoot}/built/local`
+ * - `/.lib` is a directory mapped to `${workspaceRoot}/tests/lib`
+ * - `/.src` is a virtual directory to be used for tests.
+ *
+ * Unless overridden, `/.src` will be the current working directory for the virtual file system.
+ */
+ export function createFromFileSystem(host: FileSystemResolverHost, ignoreCase: boolean, { documents, cwd }: FileSystemCreateOptions = {}) {
+ const fs = getBuiltLocal(host, ignoreCase).shadow();
+ if (cwd) {
+ fs.mkdirpSync(cwd);
+ fs.chdir(cwd);
+ }
+ if (documents) {
+ for (const document of documents) {
+ fs.mkdirpSync(vpath.dirname(document.file));
+ fs.writeFileSync(document.file, document.text, "utf8");
+ fs.filemeta(document.file).set("document", document);
+ // Add symlinks
+ const symlink = document.meta.get("symlink");
+ if (symlink) {
+ for (const link of symlink.split(",").map(link => link.trim())) {
+ fs.mkdirpSync(vpath.dirname(link));
+ fs.symlinkSync(document.file, link);
+ fs.filemeta(link).set("document", document);
+ }
+ }
+ }
+ }
+ return fs;
+ }
+
export class Stats {
public dev: number;
public ino: number;
@@ -1188,7 +1188,7 @@ namespace vfs {
source?: string;
resolver?: FileSystemResolver;
shadowRoot?: FileInode;
- meta?: core.Metadata;
+ meta?: collections.Metadata;
}
interface DirectoryInode {
@@ -1200,11 +1200,11 @@ namespace vfs {
ctimeMs: number; // status change time
birthtimeMs: number; // creation time
nlink: number; // number of hard links
- links?: core.SortedMap;
+ links?: collections.SortedMap;
source?: string;
resolver?: FileSystemResolver;
shadowRoot?: DirectoryInode;
- meta?: core.Metadata;
+ meta?: collections.Metadata;
}
interface SymlinkInode {
@@ -1218,7 +1218,7 @@ namespace vfs {
nlink: number; // number of hard links
symlink?: string;
shadowRoot?: SymlinkInode;
- meta?: core.Metadata;
+ meta?: collections.Metadata;
}
function isFile(node: Inode | undefined): node is FileInode {
@@ -1237,7 +1237,7 @@ namespace vfs {
realpath: string;
basename: string;
parent: DirectoryInode | undefined;
- links: core.SortedMap | undefined;
+ links: collections.SortedMap | undefined;
node: Inode | undefined;
}
diff --git a/src/harness/vpath.ts b/src/harness/vpath.ts
index 72c3f7d7bea..02675ccd95a 100644
--- a/src/harness/vpath.ts
+++ b/src/harness/vpath.ts
@@ -1,17 +1,16 @@
-///
-///
namespace vpath {
/**
* Virtual path separator.
*/
- export const sep = "/";
+ export import sep = ts.directorySeparator;
/**
* Normalize path separators.
*/
- export function normalizeSeparators(path: string): string {
- return path.replace(/\s*[\\/]\s*/g, sep).trim();
- }
+ export import normalizeSeparators = ts.normalizeSlashes;
+ // export function normalizeSeparators(path: string): string {
+ // return ts.normalizeSlashes(path);
+ // }
const invalidRootComponentRegExp = /^(?!(\/|\/\/\w+\/|[a-zA-Z]:\/?|)$)/;
const invalidNavigableComponentRegExp = /[:*?"<>|]/;
@@ -96,6 +95,7 @@ namespace vpath {
const absolutePathRegExp = /^[\\/]([\\/](.*?[\\/](.*?[\\/])?)?)?|^[a-zA-Z]:[\\/]?|^\w+:\/{2}[^\\/]*\/?/;
+ // 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;
@@ -105,9 +105,10 @@ namespace vpath {
* Determines whether a path is an absolute path (e.g. starts with `/`, `\\`, or a dos path
* like `c:`).
*/
- export function isAbsolute(path: string) {
- return absolutePathRegExp.test(path);
- }
+ export import isAbsolute = ts.isRootedDiskPath;
+ // export function isAbsolute(path: string) {
+ // return absolutePathRegExp.test(path);
+ // }
/**
* Determines whether a path consists only of a path root.
@@ -129,16 +130,18 @@ namespace vpath {
/**
* Adds a trailing separator (`/`) to a path if it doesn't have one.
*/
- export function addTrailingSeparator(path: string) {
- return !trailingSeperatorRegExp.test(path) && path ? path + "/" : path;
- }
+ 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 function removeTrailingSeparator(path: string) {
- return trailingSeperatorRegExp.test(path) && !isRoot(path) ? path.slice(0, -1) : path;
- }
+ 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) {
const normalized = [components[0]];
@@ -172,11 +175,12 @@ namespace vpath {
*/
export function combine(path: string, ...paths: string[]) {
path = normalizeSeparators(path);
- for (let name of paths) {
- name = normalizeSeparators(name);
- if (name.length === 0) continue;
- path = path.length === 0 || isAbsolute(name) ? name :
- addTrailingSeparator(path) + name;
+ 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;
}
@@ -188,6 +192,8 @@ namespace vpath {
return normalize(combine(path, ...paths));
}
+ // 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");
@@ -215,20 +221,23 @@ namespace vpath {
}
function relativeCaseSensitive(from: string, to: string) {
- return relativeWorker(from, to, core.equateStringsCaseSensitive);
+ return relativeWorker(from, to, ts.equateStringsCaseSensitive);
}
function relativeCaseInsensitive(from: string, to: string) {
- return relativeWorker(from, to, core.equateStringsCaseInsensitive);
+ 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);
@@ -241,32 +250,33 @@ namespace vpath {
const result = stringComparer(aComponents[i], bComponents[i]);
if (result !== 0) return result;
}
- return core.compareNumbers(aComponents.length, bComponents.length);
+ 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, core.compareStringsCaseSensitive);
+ 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, core.compareStringsCaseInsensitive);
+ 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 strings are equal.
+ * Determines whether two paths are equal.
*/
export function equals(a: string, b: string, ignoreCase: boolean) {
if (!isAbsolute(a)) throw new Error("Path not absolute");
@@ -281,6 +291,7 @@ namespace vpath {
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");
@@ -296,16 +307,17 @@ namespace vpath {
}
function beneathCaseSensitive(ancestor: string, descendant: string) {
- return beneathWorker(ancestor, descendant, core.equateStringsCaseSensitive);
+ return beneathWorker(ancestor, descendant, ts.equateStringsCaseSensitive);
}
function beneathCaseInsensitive(ancestor: string, descendant: string) {
- return beneathWorker(ancestor, descendant, core.equateStringsCaseInsensitive);
+ 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);
}
@@ -315,6 +327,9 @@ namespace vpath {
* 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);
@@ -327,6 +342,8 @@ namespace vpath {
/**
* 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) {
return components.length ? components[0] + components.slice(1).join(sep) : "";
}
@@ -334,6 +351,7 @@ namespace vpath {
/**
* 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);
@@ -349,6 +367,8 @@ namespace vpath {
* If the base name has any one of the provided extensions, it is removed.
*/
export function basename(path: string, extensions: string | ReadonlyArray, 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, ignoreCase?: boolean) {
path = normalizeSeparators(path);
path = removeTrailingSeparator(path);
@@ -384,9 +404,11 @@ namespace vpath {
* Gets the file extension for a path, provided it is one of the provided extensions.
*/
export function extname(path: string, extensions: string | ReadonlyArray, 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, ignoreCase?: boolean) {
if (extensions) {
- return extnameWorker(path, extensions, ignoreCase ? core.equateStringsCaseInsensitive : core.equateStringsCaseSensitive);
+ return extnameWorker(path, extensions, ignoreCase ? ts.equateStringsCaseInsensitive : ts.equateStringsCaseSensitive);
}
const match = extRegExp.exec(path);
@@ -395,6 +417,8 @@ namespace vpath {
export function changeExtension(path: string, ext: string): string;
export function changeExtension(path: string, ext: string, extensions: string | ReadonlyArray, 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, 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;