mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Deprecate virtualFileSystemWithWatch
This commit is contained in:
1 parent
c5e502009f
commit
c3701dfe09
12 files changed
+254
-988
No files matched your search
@@ -99,6 +99,12 @@ namespace fakes {
|
||||
? _vfs
|
||||
: new vfs.VirtualFileSystem(currentDirectory, useCaseSensitiveFileNames);
|
||||
|
||||
if (this.vfs.isReadOnly) {
|
||||
this.vfs = this.vfs.shadow();
|
||||
}
|
||||
|
||||
this.vfs.addDirectory(this.vfs.currentDirectory);
|
||||
|
||||
this.useCaseSensitiveFileNames = this.vfs.useCaseSensitiveFileNames;
|
||||
this.newLine = newLine;
|
||||
this._executingFilePath = executingFilePath;
|
||||
@@ -115,6 +121,7 @@ namespace fakes {
|
||||
dos ? FakeServerHost.dosLibPath : FakeServerHost.libPath,
|
||||
FakeServerHost.libContent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// #region DirectoryStructureHost members
|
||||
@@ -293,5 +300,42 @@ namespace fakes {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// expectations
|
||||
public checkOutputContains(expected: Iterable<string>) {
|
||||
const mapExpected = new Set(expected);
|
||||
const mapSeen = new Set<string>();
|
||||
for (const f of this._output) {
|
||||
assert.isFalse(mapSeen.has(f), `Already found ${f} in ${JSON.stringify(this._output)}`);
|
||||
if (mapExpected.has(f)) {
|
||||
mapExpected.delete(f);
|
||||
mapSeen.add(f);
|
||||
}
|
||||
}
|
||||
assert.equal(mapExpected.size, 0, `Output is missing ${JSON.stringify(ts.flatMap(Array.from(mapExpected.keys()), key => key))} in ${JSON.stringify(this._output)}`);
|
||||
}
|
||||
|
||||
public checkOutputDoesNotContain(notExpected: Iterable<string>) {
|
||||
const mapExpectedToBeAbsent = new Set(notExpected);
|
||||
for (const f of this._output) {
|
||||
assert.isFalse(mapExpectedToBeAbsent.has(f), `Contains ${f} in ${JSON.stringify(this._output)}`);
|
||||
}
|
||||
}
|
||||
|
||||
public checkWatchedFiles(expected: Iterable<string>) {
|
||||
return checkSortedSet(this.vfs.watchedFiles, expected);
|
||||
}
|
||||
|
||||
public checkWatchedDirectories(expected: Iterable<string>, recursive = false) {
|
||||
return checkSortedSet(recursive ? this.vfs.watchedRecursiveDirectories : this.vfs.watchedNonRecursiveDirectories, expected);
|
||||
}
|
||||
}
|
||||
|
||||
function checkSortedSet<T>(set: ReadonlySet<T>, values: Iterable<T>) {
|
||||
const array = Array.from(values);
|
||||
assert.strictEqual(set.size, array.length, `Actual: ${Array.from(set)}, expected: ${array}.`);
|
||||
for (const value of array) {
|
||||
assert.isTrue(set.has(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,6 @@
|
||||
"compiler.ts",
|
||||
"fakes.ts",
|
||||
|
||||
"virtualFileSystemWithWatch.ts",
|
||||
"sourceMapRecorder.ts",
|
||||
"harnessLanguageService.ts",
|
||||
"fourslash.ts",
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
namespace ts.projectSystem {
|
||||
const nullCancellationToken = server.nullCancellationToken;
|
||||
import libFile = ts.TestFSWithWatch.libFile;
|
||||
import FileOrFolder = ts.TestFSWithWatch.FileOrFolder;
|
||||
|
||||
function createTestTypingsInstaller(host: server.ServerHost) {
|
||||
return new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
|
||||
@@ -16,7 +14,7 @@ namespace ts.projectSystem {
|
||||
describe("CompileOnSave affected list", () => {
|
||||
interface ProjectFileList {
|
||||
projectFileName: string;
|
||||
files: (string | FileOrFolder | vfs.VirtualFile)[];
|
||||
files: (string | vfs.VirtualFile)[];
|
||||
}
|
||||
|
||||
function sendAffectedFileRequestAndCheckResult(session: server.Session, args: server.protocol.FileRequestArgs, expectedFileList: ProjectFileList | ProjectFileList[]) {
|
||||
@@ -64,14 +62,13 @@ namespace ts.projectSystem {
|
||||
|
||||
describe("for configured projects", () => {
|
||||
it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
const file1Consumer2 = host.vfs.addFile("/a/b/file1Consumer2.ts", `import {Foo} from "./moduleFile1"; let z = 10;`);
|
||||
host.vfs.addFile("/a/b/globalFile3.ts", `interface GlobalFoo { age: number }`);
|
||||
host.vfs.addFile("/a/b/moduleFile2.ts", `export var Foo4 = 10;`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -111,14 +108,13 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should be up-to-date with the reference map changes", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
const file1Consumer2 = host.vfs.addFile("/a/b/file1Consumer2.ts", `import {Foo} from "./moduleFile1"; let z = 10;`);
|
||||
host.vfs.addFile("/a/b/globalFile3.ts", `interface GlobalFoo { age: number }`);
|
||||
host.vfs.addFile("/a/b/moduleFile2.ts", `export var Foo4 = 10;`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -178,14 +174,13 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should be up-to-date with changes made in non-open files", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
const file1Consumer2 = host.vfs.addFile("/a/b/file1Consumer2.ts", `import {Foo} from "./moduleFile1"; let z = 10;`);
|
||||
host.vfs.addFile("/a/b/globalFile3.ts", `interface GlobalFoo { age: number }`);
|
||||
host.vfs.addFile("/a/b/moduleFile2.ts", `export var Foo4 = 10;`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -213,14 +208,13 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should be up-to-date with deleted files", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
const file1Consumer2 = host.vfs.addFile("/a/b/file1Consumer2.ts", `import {Foo} from "./moduleFile1"; let z = 10;`);
|
||||
host.vfs.addFile("/a/b/globalFile3.ts", `interface GlobalFoo { age: number }`);
|
||||
host.vfs.addFile("/a/b/moduleFile2.ts", `export var Foo4 = 10;`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -247,14 +241,13 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should be up-to-date with newly created files", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
const file1Consumer2 = host.vfs.addFile("/a/b/file1Consumer2.ts", `import {Foo} from "./moduleFile1"; let z = 10;`);
|
||||
host.vfs.addFile("/a/b/globalFile3.ts", `interface GlobalFoo { age: number }`);
|
||||
host.vfs.addFile("/a/b/moduleFile2.ts", `export var Foo4 = 10;`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -281,11 +274,10 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should detect changes in non-root files", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; let y = Foo();`);
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ "compileOnSave": true, "files": ["${file1Consumer1.path}"] }`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -325,14 +317,13 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should return all files if a global file changed shape", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
const file1Consumer2 = host.vfs.addFile("/a/b/file1Consumer2.ts", `import {Foo} from "./moduleFile1"; let z = 10;`);
|
||||
const globalFile3 = host.vfs.addFile("/a/b/globalFile3.ts", `interface GlobalFoo { age: number }`);
|
||||
const moduleFile2 = host.vfs.addFile("/a/b/moduleFile2.ts", `export var Foo4 = 10;`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -354,14 +345,13 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should return empty array if CompileOnSave is not enabled", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{}`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
host.vfs.addFile("/a/b/file1Consumer2.ts", `import {Foo} from "./moduleFile1"; let z = 10;`);
|
||||
host.vfs.addFile("/a/b/globalFile3.ts", `interface GlobalFoo { age: number }`);
|
||||
host.vfs.addFile("/a/b/moduleFile2.ts", `export var Foo4 = 10;`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -373,14 +363,13 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should save when compileOnSave is enabled in base tsconfig.json", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ "extends": "/a/tsconfig.json" }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
const file1Consumer2 = host.vfs.addFile("/a/b/file1Consumer2.ts", `import {Foo} from "./moduleFile1"; let z = 10;`);
|
||||
host.vfs.addFile("/a/b/globalFile3.ts", `interface GlobalFoo { age: number }`);
|
||||
host.vfs.addFile("/a/b/moduleFile2.ts", `export var Foo4 = 10;`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
host.vfs.addFile("/a/tsconfig.json", `{ "compileOnSave": true }`);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
@@ -393,11 +382,10 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should always return the file itself if '--isolatedModules' is specified", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ "compileOnSave": true, "compilerOptions": { "isolatedModules": true } }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -418,11 +406,10 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should always return the file itself if '--out' or '--outFile' is specified", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ "compileOnSave": true, "compilerOptions": { "module": "system", "outFile": "/a/b/out.js" } }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -443,13 +430,12 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should return cascaded affected file list", () => {
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
const file1Consumer1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1Consumer1.ts", `import {y} from "./file1Consumer1";`);
|
||||
host.vfs.addFile("/a/b/globalFile3.ts", `interface GlobalFoo { age: number }`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host, createTestTypingsInstaller(host));
|
||||
|
||||
@@ -574,11 +560,10 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should emit specified file", () => {
|
||||
const host = new fakes.FakeServerHost({ newLine: "\r\n" });
|
||||
const host = new fakes.FakeServerHost({ lib: true, newLine: "\r\n" });
|
||||
const file1 = host.vfs.addFile("/a/b/f1.ts", `export function Foo() { return 10; }`);
|
||||
const file2 = host.vfs.addFile("/a/b/f2.ts", `import {Foo} from "./f1"; let y = Foo();`);
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{}`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const typingsInstaller = createTestTypingsInstaller(host);
|
||||
const session = createSession(host, { typingsInstaller });
|
||||
@@ -595,12 +580,11 @@ namespace ts.projectSystem {
|
||||
it("shoud not emit js files in external projects", () => {
|
||||
const externalProjectName = "/a/b/externalproject";
|
||||
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const file1 = host.vfs.addFile("/a/b/file1.ts", `consonle.log('file1');`);
|
||||
// file2 has errors. The emit should not be blocked.
|
||||
const file2 = host.vfs.addFile("/a/b/file2.js", `console.log'file2');`);
|
||||
const file3 = host.vfs.addFile("/a/b/file3.js", `console.log('file3');`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host);
|
||||
const projectService = session.getProjectService();
|
||||
@@ -628,9 +612,8 @@ namespace ts.projectSystem {
|
||||
it("should use project root as current directory so that compile on save results in correct file mapping", () => {
|
||||
const externalProjectName = "/root/TypeScriptProject3/TypeScriptProject3/TypeScriptProject3.csproj";
|
||||
|
||||
const host = new fakes.FakeServerHost();
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
const file1 = host.vfs.addFile("/root/TypeScriptProject3/TypeScriptProject3/Foo.ts", `consonle.log('file1');`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
const session = createSession(host);
|
||||
const projectService = session.getProjectService();
|
||||
|
||||
@@ -99,19 +99,6 @@ namespace ts {
|
||||
getCurrentDirectory: notImplemented,
|
||||
};
|
||||
|
||||
function createServerHost(files: ts.TestFSWithWatch.FileOrFolder[], options?: Partial<fakes.FakeServerHostOptions>) {
|
||||
const host = new fakes.FakeServerHost(options);
|
||||
for (const file of files) {
|
||||
if (isString(file.content)) {
|
||||
host.vfs.writeFile(file.path, file.content);
|
||||
}
|
||||
else {
|
||||
host.vfs.addDirectory(file.path);
|
||||
}
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
export function testExtractSymbol(caption: string, text: string, baselineFolder: string, description: DiagnosticMessage, includeLib?: boolean) {
|
||||
const t = extractTest(text);
|
||||
const selectionRange = t.ranges.get("selection");
|
||||
@@ -168,7 +155,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function makeProgram(f: {path: string, content: string }, includeLib?: boolean) {
|
||||
const host = createServerHost(includeLib ? [f, ts.TestFSWithWatch.libFile] : [f]); // libFile is expensive to parse repeatedly - only test when required
|
||||
const host = new fakes.FakeServerHost({ lib: includeLib }); // libFile is expensive to parse repeatedly - only test when required
|
||||
host.vfs.writeFile(f.path, f.content);
|
||||
const projectService = projectSystem.createProjectService(host);
|
||||
projectService.openClientFile(f.path);
|
||||
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
|
||||
@@ -188,15 +176,12 @@ namespace ts {
|
||||
if (!selectionRange) {
|
||||
throw new Error(`Test ${caption} does not specify selection range`);
|
||||
}
|
||||
const f = {
|
||||
path: "/a.ts",
|
||||
content: t.source
|
||||
};
|
||||
const host = ts.TestFSWithWatch.createServerHost([f, ts.TestFSWithWatch.libFile]);
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.writeFile("/a.ts", t.source);
|
||||
const projectService = projectSystem.createProjectService(host);
|
||||
projectService.openClientFile(f.path);
|
||||
projectService.openClientFile("/a.ts");
|
||||
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
|
||||
const sourceFile = program.getSourceFile(f.path);
|
||||
const sourceFile = program.getSourceFile("/a.ts");
|
||||
const context: RefactorContext = {
|
||||
cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse },
|
||||
newLineCharacter,
|
||||
|
||||
@@ -872,8 +872,6 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
import libFile = ts.TestFSWithWatch.libFile;
|
||||
|
||||
describe("isProgramUptoDate should return true when there is no change in compiler options and", () => {
|
||||
function verifyProgramIsUptoDate(
|
||||
program: Program,
|
||||
@@ -926,7 +924,7 @@ namespace ts {
|
||||
const file1 = fs.addFile("/a/b/file1.ts", "let x = 1");
|
||||
const file2 = fs.addFile("/a/b/file2.ts", "let y = 1");
|
||||
const configFile = fs.addFile("/a/b/tsconfig.json", "{}");
|
||||
fs.addFile(libFile.path, libFile.content);
|
||||
fs.addFile(fakes.FakeServerHost.libPath, fakes.FakeServerHost.libContent);
|
||||
verifyProgram(fs, [file1.path, file2.path], {}, configFile.path);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,48 +1,56 @@
|
||||
/// <reference path="../harness.ts" />
|
||||
/// <reference path="./tsserverProjectSystem.ts" />
|
||||
/// <reference path="../fakes.ts" />
|
||||
|
||||
namespace ts.projectSystem {
|
||||
import FileOrFolder = ts.TestFSWithWatch.FileOrFolder;
|
||||
|
||||
describe("project telemetry", () => {
|
||||
it("does nothing for inferred project", () => {
|
||||
const file = makeFile("/a.js");
|
||||
const et = new TestServerEventManager([file]);
|
||||
et.service.openClientFile(file.path);
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.writeFile("/a.js", "");
|
||||
const et = new TestServerEventManager(host);
|
||||
et.service.openClientFile("/a.js");
|
||||
et.hasZeroEvent(ts.server.ProjectInfoTelemetryEvent);
|
||||
});
|
||||
|
||||
it("only sends an event once", () => {
|
||||
const file = makeFile("/a/a.ts");
|
||||
const file2 = makeFile("/b.ts");
|
||||
const tsconfig = makeFile("/a/tsconfig.json", {});
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.writeFile("/a/a.ts", ``);
|
||||
host.vfs.writeFile("/b.ts", ``);
|
||||
host.vfs.writeFile("/a/tsconfig.json", `{}`);
|
||||
|
||||
const et = new TestServerEventManager([file, file2, tsconfig]);
|
||||
et.service.openClientFile(file.path);
|
||||
et.assertProjectInfoTelemetryEvent({}, tsconfig.path);
|
||||
const et = new TestServerEventManager(host);
|
||||
et.service.openClientFile("/a/a.ts");
|
||||
et.assertProjectInfoTelemetryEvent({}, "/a/tsconfig.json");
|
||||
|
||||
et.service.closeClientFile(file.path);
|
||||
et.service.closeClientFile("/a/a.ts");
|
||||
checkNumberOfProjects(et.service, { configuredProjects: 1 });
|
||||
|
||||
et.service.openClientFile(file2.path);
|
||||
et.service.openClientFile("/b.ts");
|
||||
checkNumberOfProjects(et.service, { inferredProjects: 1 });
|
||||
|
||||
et.hasZeroEvent(ts.server.ProjectInfoTelemetryEvent);
|
||||
|
||||
et.service.openClientFile(file.path);
|
||||
et.service.openClientFile("/a/a.ts");
|
||||
checkNumberOfProjects(et.service, { configuredProjects: 1, inferredProjects: 1 });
|
||||
|
||||
et.hasZeroEvent(ts.server.ProjectInfoTelemetryEvent);
|
||||
});
|
||||
|
||||
it("counts files by extension", () => {
|
||||
const files = ["ts.ts", "tsx.tsx", "moo.ts", "dts.d.ts", "jsx.jsx", "js.js", "badExtension.badExtension"].map(f => makeFile(`/src/${f}`));
|
||||
const notIncludedFile = makeFile("/bin/ts.js");
|
||||
const compilerOptions: ts.CompilerOptions = { allowJs: true };
|
||||
const tsconfig = makeFile("/tsconfig.json", { compilerOptions, include: ["src"] });
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.writeFile("/src/ts.ts", ``);
|
||||
host.vfs.writeFile("/src/tsx.tsx", ``);
|
||||
host.vfs.writeFile("/src/moo.ts", ``);
|
||||
host.vfs.writeFile("/src/dts.d.ts", ``);
|
||||
host.vfs.writeFile("/src/jsx.jsx", ``);
|
||||
host.vfs.writeFile("/src/js.js", ``);
|
||||
host.vfs.writeFile("/src/badExtension.badExtension", ``);
|
||||
host.vfs.writeFile("/bin/ts.js", ``);
|
||||
host.vfs.writeFile("/tsconfig.json", JSON.stringify({ compilerOptions, include: ["src"] }));
|
||||
|
||||
const et = new TestServerEventManager([...files, notIncludedFile, tsconfig]);
|
||||
et.service.openClientFile(files[0].path);
|
||||
const et = new TestServerEventManager(host);
|
||||
et.service.openClientFile("/src/ts.ts");
|
||||
et.assertProjectInfoTelemetryEvent({
|
||||
fileStats: { ts: 2, tsx: 1, js: 1, jsx: 1, dts: 1 },
|
||||
compilerOptions,
|
||||
@@ -51,8 +59,10 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("works with external project", () => {
|
||||
const file1 = makeFile("/a.ts");
|
||||
const et = new TestServerEventManager([file1]);
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.writeFile("/a.ts", ``);
|
||||
|
||||
const et = new TestServerEventManager(host);
|
||||
const compilerOptions: ts.server.protocol.CompilerOptions = { strict: true };
|
||||
|
||||
const projectFileName = "/hunter2/foo.csproj";
|
||||
@@ -61,7 +71,7 @@ namespace ts.projectSystem {
|
||||
|
||||
// TODO: Apparently compilerOptions is mutated, so have to repeat it here!
|
||||
et.assertProjectInfoTelemetryEvent({
|
||||
projectId: Harness.mockHash("/hunter2/foo.csproj"),
|
||||
projectId: core.sha1("/hunter2/foo.csproj"),
|
||||
compilerOptions: { strict: true },
|
||||
compileOnSave: true,
|
||||
// These properties can't be present for an external project, so they are undefined instead of false.
|
||||
@@ -83,7 +93,7 @@ namespace ts.projectSystem {
|
||||
|
||||
function open(): void {
|
||||
et.service.openExternalProject({
|
||||
rootFiles: toExternalFiles([file1.path]),
|
||||
rootFiles: toExternalFiles(["/a.ts"]),
|
||||
options: compilerOptions,
|
||||
projectFileName,
|
||||
});
|
||||
@@ -92,8 +102,6 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("does not expose paths", () => {
|
||||
const file = makeFile("/a.ts");
|
||||
|
||||
const compilerOptions: ts.CompilerOptions = {
|
||||
project: "",
|
||||
outFile: "hunter2.js",
|
||||
@@ -148,10 +156,13 @@ namespace ts.projectSystem {
|
||||
lib: ["es6", "dom"],
|
||||
};
|
||||
(compilerOptions as any).unknownCompilerOption = "hunter2"; // These are always ignored.
|
||||
const tsconfig = makeFile("/tsconfig.json", { compilerOptions, files: ["/a.ts"] });
|
||||
|
||||
const et = new TestServerEventManager([file, tsconfig]);
|
||||
et.service.openClientFile(file.path);
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.writeFile("/a.ts", ``);
|
||||
host.vfs.writeFile("/tsconfig.json", JSON.stringify({ compilerOptions, files: ["/a.ts"] }));
|
||||
|
||||
const et = new TestServerEventManager(host);
|
||||
et.service.openClientFile("/a.ts");
|
||||
|
||||
et.assertProjectInfoTelemetryEvent({
|
||||
compilerOptions: safeCompilerOptions,
|
||||
@@ -160,18 +171,19 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("sends telemetry for extends, files, include, exclude, and compileOnSave", () => {
|
||||
const file = makeFile("/hunter2/a.ts");
|
||||
const tsconfig = makeFile("/tsconfig.json", {
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.writeFile("/hunter2/a.ts", ``);
|
||||
host.vfs.writeFile("/tsconfig.json", JSON.stringify({
|
||||
compilerOptions: {},
|
||||
extends: "hunter2.json",
|
||||
files: ["hunter2/a.ts"],
|
||||
include: ["hunter2"],
|
||||
exclude: ["hunter2"],
|
||||
compileOnSave: true,
|
||||
});
|
||||
}));
|
||||
|
||||
const et = new TestServerEventManager([tsconfig, file]);
|
||||
et.service.openClientFile(file.path);
|
||||
const et = new TestServerEventManager(host);
|
||||
et.service.openClientFile("/hunter2/a.ts");
|
||||
et.assertProjectInfoTelemetryEvent({
|
||||
extends: true,
|
||||
files: true,
|
||||
@@ -190,8 +202,9 @@ namespace ts.projectSystem {
|
||||
};
|
||||
|
||||
it("sends telemetry for typeAcquisition settings", () => {
|
||||
const file = makeFile("/a.js");
|
||||
const jsconfig = makeFile("/jsconfig.json", {
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.writeFile("/a.js", ``);
|
||||
host.vfs.writeFile("/jsconfig.json", JSON.stringify({
|
||||
compilerOptions: {},
|
||||
typeAcquisition: {
|
||||
enable: true,
|
||||
@@ -199,11 +212,11 @@ namespace ts.projectSystem {
|
||||
include: ["hunter2", "hunter3"],
|
||||
exclude: [],
|
||||
},
|
||||
});
|
||||
const et = new TestServerEventManager([jsconfig, file]);
|
||||
et.service.openClientFile(file.path);
|
||||
}));
|
||||
const et = new TestServerEventManager(host);
|
||||
et.service.openClientFile("/a.js");
|
||||
et.assertProjectInfoTelemetryEvent({
|
||||
projectId: Harness.mockHash("/jsconfig.json"),
|
||||
projectId: core.sha1("/jsconfig.json"),
|
||||
fileStats: fileStats({ js: 1 }),
|
||||
compilerOptions: autoJsCompilerOptions,
|
||||
typeAcquisition: {
|
||||
@@ -216,14 +229,16 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("detects whether language service was disabled", () => {
|
||||
const file = makeFile("/a.js");
|
||||
const tsconfig = makeFile("/jsconfig.json", {});
|
||||
const et = new TestServerEventManager([tsconfig, file]);
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.writeFile("/a.js", ``);
|
||||
host.vfs.writeFile("/jsconfig.json", `{}`);
|
||||
|
||||
const et = new TestServerEventManager(host);
|
||||
et.host.getFileSize = () => server.maxProgramSizeForNonTsFiles + 1;
|
||||
et.service.openClientFile(file.path);
|
||||
et.service.openClientFile("/a.js");
|
||||
et.getEvent<server.ProjectLanguageServiceStateEvent>(server.ProjectLanguageServiceStateEvent);
|
||||
et.assertProjectInfoTelemetryEvent({
|
||||
projectId: Harness.mockHash("/jsconfig.json"),
|
||||
projectId: core.sha1("/jsconfig.json"),
|
||||
fileStats: fileStats({ js: 1 }),
|
||||
compilerOptions: autoJsCompilerOptions,
|
||||
configFileName: "jsconfig.json",
|
||||
@@ -236,8 +251,4 @@ namespace ts.projectSystem {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function makeFile(path: string, content: {} = ""): FileOrFolder {
|
||||
return { path, content: isString(content) ? "" : JSON.stringify(content) };
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\..\compiler\watch.ts" />
|
||||
/// <reference path="..\virtualFileSystemWithWatch.ts" />
|
||||
/// <reference path="../harness.ts" />
|
||||
/// <reference path="../../compiler/watch.ts" />
|
||||
/// <reference path="../fakes.ts" />
|
||||
/// <reference path="../vpath.ts" />
|
||||
/// <reference path="../utils.ts" />
|
||||
|
||||
namespace ts.tscWatch {
|
||||
import theory = utils.theory;
|
||||
@@ -10,11 +10,7 @@ namespace ts.tscWatch {
|
||||
import Arg = typemock.Arg;
|
||||
import Times = typemock.Times;
|
||||
|
||||
import checkFileNames = ts.TestFSWithWatch.checkFileNames;
|
||||
import checkWatchedFiles = ts.TestFSWithWatch.checkWatchedFiles;
|
||||
import checkWatchedDirectories = ts.TestFSWithWatch.checkWatchedDirectories;
|
||||
import checkOutputContains = ts.TestFSWithWatch.checkOutputContains;
|
||||
import checkOutputDoesNotContain = ts.TestFSWithWatch.checkOutputDoesNotContain;
|
||||
import checkFileNames = utils.checkFileNames;
|
||||
|
||||
export function checkProgramActualFiles(program: Program, expectedFiles: string[]) {
|
||||
checkFileNames(`Program actual files`, program.getSourceFiles().map(file => file.fileName), expectedFiles);
|
||||
@@ -197,8 +193,8 @@ namespace ts.tscWatch {
|
||||
|
||||
checkProgramActualFiles(watch(), ["/a/b/c/f1.ts", fakes.FakeServerHost.libPath, "/a/b/d/f2.ts"]);
|
||||
checkProgramRootFiles(watch(), ["/a/b/c/f1.ts", "/a/b/d/f2.ts"]);
|
||||
checkWatchedFiles(host, ["/a/b/tsconfig.json", "/a/b/c/f1.ts", "/a/b/d/f2.ts", fakes.FakeServerHost.libPath]);
|
||||
checkWatchedDirectories(host, ["/a/b", "/a/b/node_modules/@types"], /*recursive*/ true);
|
||||
host.checkWatchedFiles(["/a/b/tsconfig.json", "/a/b/c/f1.ts", "/a/b/d/f2.ts", fakes.FakeServerHost.libPath]);
|
||||
host.checkWatchedDirectories(["/a/b", "/a/b/node_modules/@types"], /*recursive*/ true);
|
||||
});
|
||||
|
||||
// TODO: if watching for config file creation
|
||||
@@ -211,7 +207,7 @@ namespace ts.tscWatch {
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{}`);
|
||||
|
||||
const watch = createWatchModeWithConfigFile("/a/b/tsconfig.json", host);
|
||||
checkWatchedDirectories(host, ["/a/b", "/a/b/node_modules/@types"], /*recursive*/ true);
|
||||
host.checkWatchedDirectories(["/a/b", "/a/b/node_modules/@types"], /*recursive*/ true);
|
||||
checkProgramRootFiles(watch(), ["/a/b/commonFile1.ts"]);
|
||||
|
||||
// add a new ts file
|
||||
@@ -767,11 +763,11 @@ namespace ts.tscWatch {
|
||||
|
||||
function checkAffectedFiles(host: fakes.FakeServerHost, affectedFiles: ReadonlyArray<string>, unaffectedFiles?: ReadonlyArray<string>) {
|
||||
affectedFiles = getEmittedLines(affectedFiles, host, formatOutputFile);
|
||||
checkOutputContains(host, affectedFiles);
|
||||
host.checkOutputContains(affectedFiles);
|
||||
if (unaffectedFiles) {
|
||||
unaffectedFiles = getEmittedLines(unaffectedFiles, host, formatOutputFile);
|
||||
unaffectedFiles = mapDefined(unaffectedFiles, line => contains(affectedFiles, line) ? undefined : line);
|
||||
checkOutputDoesNotContain(host, unaffectedFiles);
|
||||
host.checkOutputDoesNotContain(unaffectedFiles);
|
||||
}
|
||||
host.clearOutput();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\virtualFileSystemWithWatch.ts" />
|
||||
/// <reference path="../harness.ts" />
|
||||
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
|
||||
/// <reference path="../utils.ts" />
|
||||
/// <reference path="../fakes.ts" />
|
||||
@@ -12,12 +11,14 @@ namespace ts.projectSystem {
|
||||
import protocol = server.protocol;
|
||||
import CommandNames = server.CommandNames;
|
||||
|
||||
import FileOrFolder = ts.TestFSWithWatch.FileOrFolder;
|
||||
import checkFileNames = ts.TestFSWithWatch.checkFileNames;
|
||||
import libFile = ts.TestFSWithWatch.libFile;
|
||||
import checkWatchedFiles = ts.TestFSWithWatch.checkWatchedFiles;
|
||||
import checkWatchedDirectories = ts.TestFSWithWatch.checkWatchedDirectories;
|
||||
import safeList = ts.TestFSWithWatch.safeList;
|
||||
interface FileOrFolder {
|
||||
path: string;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
import checkFileNames = utils.checkFileNames;
|
||||
|
||||
const libFile = { path: fakes.FakeServerHost.libPath, content: fakes.FakeServerHost.libContent };
|
||||
|
||||
export const customTypesMap = {
|
||||
path: <Path>"/typesMap.json",
|
||||
@@ -68,7 +69,7 @@ namespace ts.projectSystem {
|
||||
installTypingHost: server.ServerHost,
|
||||
readonly typesRegistry = createMap<void>(),
|
||||
log?: TI.Log) {
|
||||
super(installTypingHost, globalTypingsCacheLocation, safeList.path, customTypesMap.path, throttleLimit, log);
|
||||
super(installTypingHost, globalTypingsCacheLocation, <Path>fakes.FakeServerHost.safeListPath, customTypesMap.path, throttleLimit, log);
|
||||
}
|
||||
|
||||
protected postExecActions: PostExecAction[] = [];
|
||||
@@ -146,8 +147,8 @@ namespace ts.projectSystem {
|
||||
readonly session: TestSession;
|
||||
readonly service: server.ProjectService;
|
||||
readonly host: fakes.FakeServerHost;
|
||||
constructor(files: FileOrFolder[]) {
|
||||
this.host = createServerHost(files);
|
||||
constructor(host: fakes.FakeServerHost) {
|
||||
this.host = host;
|
||||
this.session = createSession(this.host, {
|
||||
canUseEvents: true,
|
||||
eventHandler: event => this.events.push(event),
|
||||
@@ -189,7 +190,7 @@ namespace ts.projectSystem {
|
||||
|
||||
assertProjectInfoTelemetryEvent(partial: Partial<server.ProjectInfoTelemetryEventData>, configFile?: string): void {
|
||||
assert.deepEqual(this.getEvent<server.ProjectInfoTelemetryEvent>(ts.server.ProjectInfoTelemetryEvent), {
|
||||
projectId: Harness.mockHash(configFile || "/tsconfig.json"),
|
||||
projectId: core.sha1(configFile || "/tsconfig.json"),
|
||||
fileStats: fileStats({ ts: 1 }),
|
||||
compilerOptions: {},
|
||||
extends: false,
|
||||
@@ -517,6 +518,37 @@ namespace ts.projectSystem {
|
||||
return host;
|
||||
}
|
||||
|
||||
function getDiffInKeys<T>(map: Map<T>, expectedKeys: ReadonlyArray<string>) {
|
||||
if (map.size === expectedKeys.length) {
|
||||
return "";
|
||||
}
|
||||
const notInActual: string[] = [];
|
||||
const duplicates: string[] = [];
|
||||
const seen = createMap<true>();
|
||||
forEach(expectedKeys, expectedKey => {
|
||||
if (seen.has(expectedKey)) {
|
||||
duplicates.push(expectedKey);
|
||||
return;
|
||||
}
|
||||
seen.set(expectedKey, true);
|
||||
if (!map.has(expectedKey)) {
|
||||
notInActual.push(expectedKey);
|
||||
}
|
||||
});
|
||||
const inActualNotExpected: string[] = [];
|
||||
map.forEach((_value, key) => {
|
||||
if (!seen.has(key)) {
|
||||
inActualNotExpected.push(key);
|
||||
}
|
||||
seen.set(key, true);
|
||||
});
|
||||
return `\n\nNotInActual: ${notInActual}\nDuplicates: ${duplicates}\nInActualButNotInExpected: ${inActualNotExpected}`;
|
||||
}
|
||||
|
||||
function verifyMapSize(caption: string, map: Map<any>, expectedKeys: ReadonlyArray<string>) {
|
||||
assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map: Actual keys: ${arrayFrom(map.keys())} Expected: ${expectedKeys}${getDiffInKeys(map, expectedKeys)}`);
|
||||
}
|
||||
|
||||
describe("tsserverProjectSystem", () => {
|
||||
describe("general", () => {
|
||||
const commonFile1: FileOrFolder = {
|
||||
@@ -545,9 +577,9 @@ namespace ts.projectSystem {
|
||||
checkFileNames("inferred project", project.getFileNames(), ["/a/b/c/app.ts", fakes.FakeServerHost.libPath, "/a/b/c/module.d.ts"]);
|
||||
const configFileLocations = ["/a/b/c/", "/a/b/", "/a/", "/"];
|
||||
const configFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]);
|
||||
checkWatchedFiles(host, configFiles.concat(fakes.FakeServerHost.libPath, "/a/b/c/module.d.ts"));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, ["/a/b/c", combinePaths("/a/b/c/", nodeModulesAtTypes)], /*recursive*/ true);
|
||||
host.checkWatchedFiles(configFiles.concat(fakes.FakeServerHost.libPath, "/a/b/c/module.d.ts"));
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
host.checkWatchedDirectories(["/a/b/c", combinePaths("/a/b/c/", nodeModulesAtTypes)], /*recursive*/ true);
|
||||
});
|
||||
|
||||
it("can handle tsconfig file name with difference casing", () => {
|
||||
@@ -608,9 +640,9 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(project, [file1.path, fakes.FakeServerHost.libPath, file2.path, configFile.path]);
|
||||
checkProjectRootFiles(project, [file1.path, file2.path]);
|
||||
// watching all files except one that was open
|
||||
checkWatchedFiles(host, [configFile.path, file2.path, fakes.FakeServerHost.libPath]);
|
||||
host.checkWatchedFiles([configFile.path, file2.path, fakes.FakeServerHost.libPath]);
|
||||
const configFileDirectory = getDirectoryPath(configFile.path);
|
||||
checkWatchedDirectories(host, [configFileDirectory, combinePaths(configFileDirectory, nodeModulesAtTypes)], /*recursive*/ true);
|
||||
host.checkWatchedDirectories([configFileDirectory, combinePaths(configFileDirectory, nodeModulesAtTypes)], /*recursive*/ true);
|
||||
});
|
||||
|
||||
it("create configured project with the file list", () => {
|
||||
@@ -648,8 +680,8 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(project, [file1.path, fakes.FakeServerHost.libPath, file2.path, configFile.path]);
|
||||
checkProjectRootFiles(project, [file1.path, file2.path]);
|
||||
// watching all files except one that was open
|
||||
checkWatchedFiles(host, [configFile.path, file2.path, fakes.FakeServerHost.libPath]);
|
||||
checkWatchedDirectories(host, [getDirectoryPath(configFile.path)], /*recursive*/ false);
|
||||
host.checkWatchedFiles([configFile.path, file2.path, fakes.FakeServerHost.libPath]);
|
||||
host.checkWatchedDirectories([getDirectoryPath(configFile.path)], /*recursive*/ false);
|
||||
});
|
||||
|
||||
it("add and then remove a config file in a folder with loose files", () => {
|
||||
@@ -670,14 +702,14 @@ namespace ts.projectSystem {
|
||||
checkNumberOfInferredProjects(projectService, 2);
|
||||
const configFileLocations = ["/", "/a/", "/a/b/"];
|
||||
const watchedFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]).concat(fakes.FakeServerHost.libPath);
|
||||
checkWatchedFiles(host, watchedFiles);
|
||||
host.checkWatchedFiles(watchedFiles);
|
||||
|
||||
// Add a tsconfig file
|
||||
host.vfs.addFile(configFile.path, configFile.content);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
checkWatchedFiles(host, watchedFiles);
|
||||
host.checkWatchedFiles(watchedFiles);
|
||||
|
||||
// remove the tsconfig file
|
||||
host.vfs.removeFile(configFile.path);
|
||||
@@ -687,7 +719,7 @@ namespace ts.projectSystem {
|
||||
|
||||
checkNumberOfInferredProjects(projectService, 2);
|
||||
checkNumberOfConfiguredProjects(projectService, 0);
|
||||
checkWatchedFiles(host, watchedFiles);
|
||||
host.checkWatchedFiles(watchedFiles);
|
||||
});
|
||||
|
||||
it("add new files to a configured project without file list", () => {
|
||||
@@ -699,7 +731,7 @@ namespace ts.projectSystem {
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(commonFile1.path);
|
||||
const configFileDir = getDirectoryPath(configFile.path);
|
||||
checkWatchedDirectories(host, [configFileDir, combinePaths(configFileDir, nodeModulesAtTypes)], /*recursive*/ true);
|
||||
host.checkWatchedDirectories([configFileDir, combinePaths(configFileDir, nodeModulesAtTypes)], /*recursive*/ true);
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
|
||||
const project = configuredProjectAt(projectService, 0);
|
||||
@@ -2118,7 +2150,7 @@ namespace ts.projectSystem {
|
||||
projectService.openExternalProject({ projectFileName, options: {}, rootFiles: [{ fileName: file1.path, scriptKind: ScriptKind.JS, hasMixedContent: true }] });
|
||||
|
||||
checkNumberOfProjects(projectService, { externalProjects: 1 });
|
||||
checkWatchedFiles(host, [fakes.FakeServerHost.libPath]); // watching the "missing" lib file
|
||||
host.checkWatchedFiles([fakes.FakeServerHost.libPath]); // watching the "missing" lib file
|
||||
|
||||
const project = projectService.externalProjects[0];
|
||||
|
||||
@@ -2762,11 +2794,11 @@ namespace ts.projectSystem {
|
||||
const project = projectService.configuredProjects.get(configFile.path);
|
||||
assert.isDefined(project);
|
||||
checkProjectActualFiles(project, map(files, file => file.path));
|
||||
checkWatchedFiles(host, mapDefined(files, file => file === file1 ? undefined : file.path));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
host.checkWatchedFiles(mapDefined(files, file => file === file1 ? undefined : file.path));
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
const watchedRecursiveDirectories = ["/a/b/node_modules/@types"];
|
||||
watchedRecursiveDirectories.push("/a/b");
|
||||
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
|
||||
host.checkWatchedDirectories(watchedRecursiveDirectories, /*recursive*/ true);
|
||||
|
||||
files.push(file2);
|
||||
host.vfs.writeFile(file2.path, file2.content);
|
||||
@@ -2775,18 +2807,18 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
assert.strictEqual(projectService.configuredProjects.get(configFile.path), project);
|
||||
checkProjectActualFiles(project, mapDefined(files, file => file === file2a ? undefined : file.path));
|
||||
checkWatchedFiles(host, mapDefined(files, file => file === file1 ? undefined : file.path));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
|
||||
host.checkWatchedFiles(mapDefined(files, file => file === file1 ? undefined : file.path));
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
host.checkWatchedDirectories(watchedRecursiveDirectories, /*recursive*/ true);
|
||||
|
||||
// On next file open the files file2a should be closed and not watched any more
|
||||
projectService.openClientFile(file2.path);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
assert.strictEqual(projectService.configuredProjects.get(configFile.path), project);
|
||||
checkProjectActualFiles(project, mapDefined(files, file => file === file2a ? undefined : file.path));
|
||||
checkWatchedFiles(host, [fakes.FakeServerHost.libPath, configFile.path]);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
|
||||
host.checkWatchedFiles([fakes.FakeServerHost.libPath, configFile.path]);
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
host.checkWatchedDirectories(watchedRecursiveDirectories, /*recursive*/ true);
|
||||
|
||||
});
|
||||
|
||||
@@ -2823,11 +2855,11 @@ namespace ts.projectSystem {
|
||||
const project = projectService.configuredProjects.get(configFile.path);
|
||||
assert.isDefined(project);
|
||||
checkProjectActualFiles(project, [file1.path, fakes.FakeServerHost.libPath, module1.path, module2.path, configFile.path]);
|
||||
checkWatchedFiles(host, [fakes.FakeServerHost.libPath, module1.path, module2.path, configFile.path]);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
host.checkWatchedFiles([fakes.FakeServerHost.libPath, module1.path, module2.path, configFile.path]);
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
const watchedRecursiveDirectories = getTypeRootsFromLocation(root + "/a/b/src");
|
||||
watchedRecursiveDirectories.push(`${root}/a/b/src`, `${root}/a/b/node_modules`);
|
||||
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
|
||||
host.checkWatchedDirectories(watchedRecursiveDirectories, /*recursive*/ true);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -3632,10 +3664,9 @@ namespace ts.projectSystem {
|
||||
path: "/a/b/node_modules/@types/node/index.d.ts",
|
||||
content: "declare var process: any"
|
||||
};
|
||||
const cwd = {
|
||||
path: "/a/c"
|
||||
};
|
||||
const host = createServerHost([f1, config, node, cwd], { vfs: { currentDirectory: cwd.path } });
|
||||
const host = createServerHost([f1, config, node], { vfs: { currentDirectory: "/a/c" } });
|
||||
host.vfs.addDirectory("/a/c");
|
||||
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
@@ -3769,7 +3800,8 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}`
|
||||
};
|
||||
const serverEventManager = new TestServerEventManager([file, configFile]);
|
||||
const host = createServerHost([file, configFile]);
|
||||
const serverEventManager = new TestServerEventManager(host);
|
||||
openFilesForSession([file], serverEventManager.session);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
|
||||
});
|
||||
@@ -3785,7 +3817,8 @@ namespace ts.projectSystem {
|
||||
"compilerOptions": {}
|
||||
}`
|
||||
};
|
||||
const serverEventManager = new TestServerEventManager([file, configFile]);
|
||||
const host = createServerHost([file, configFile]);
|
||||
const serverEventManager = new TestServerEventManager(host);
|
||||
openFilesForSession([file], serverEventManager.session);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
|
||||
});
|
||||
@@ -3802,7 +3835,8 @@ namespace ts.projectSystem {
|
||||
}`
|
||||
};
|
||||
|
||||
const serverEventManager = new TestServerEventManager([file, configFile]);
|
||||
const host = createServerHost([file, configFile]);
|
||||
const serverEventManager = new TestServerEventManager(host);
|
||||
openFilesForSession([file], serverEventManager.session);
|
||||
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
|
||||
|
||||
@@ -3842,7 +3876,8 @@ namespace ts.projectSystem {
|
||||
"files": ["app.ts"]
|
||||
}`
|
||||
};
|
||||
const serverEventManager = new TestServerEventManager([file, file2, libFile, configFile]);
|
||||
const host = createServerHost([file, file2, libFile, configFile]);
|
||||
const serverEventManager = new TestServerEventManager(host);
|
||||
openFilesForSession([file2], serverEventManager.session);
|
||||
serverEventManager.hasZeroEvent("configFileDiag");
|
||||
});
|
||||
@@ -3863,7 +3898,8 @@ namespace ts.projectSystem {
|
||||
}`
|
||||
};
|
||||
|
||||
const serverEventManager = new TestServerEventManager([file, file2, libFile, configFile]);
|
||||
const host = createServerHost([file, file2, libFile, configFile]);
|
||||
const serverEventManager = new TestServerEventManager(host);
|
||||
openFilesForSession([file2], serverEventManager.session);
|
||||
serverEventManager.hasZeroEvent("configFileDiag");
|
||||
});
|
||||
@@ -4710,18 +4746,18 @@ namespace ts.projectSystem {
|
||||
service.openClientFile(f1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, projectDir);
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
assert.isDefined(service.configuredProjects.get(configFile.path));
|
||||
checkWatchedFiles(host, [fakes.FakeServerHost.libPath, configFile.path]);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
host.checkWatchedFiles([fakes.FakeServerHost.libPath, configFile.path]);
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
const typeRootLocations = getTypeRootsFromLocation(configFileLocation);
|
||||
checkWatchedDirectories(host, typeRootLocations.concat(configFileLocation), /*recursive*/ true);
|
||||
host.checkWatchedDirectories(typeRootLocations.concat(configFileLocation), /*recursive*/ true);
|
||||
|
||||
// Delete config file - should create inferred project and not configured project
|
||||
host.vfs.removeFile(configFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkNumberOfProjects(service, { inferredProjects: 1 });
|
||||
checkWatchedFiles(host, [fakes.FakeServerHost.libPath, configFile.path, `${configFileLocation}/jsconfig.json`, `${projectDir}/tsconfig.json`, `${projectDir}/jsconfig.json`]);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, typeRootLocations, /*recursive*/ true);
|
||||
host.checkWatchedFiles([fakes.FakeServerHost.libPath, configFile.path, `${configFileLocation}/jsconfig.json`, `${projectDir}/tsconfig.json`, `${projectDir}/jsconfig.json`]);
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
host.checkWatchedDirectories(typeRootLocations, /*recursive*/ true);
|
||||
});
|
||||
|
||||
it("should use projectRootPath when searching for inferred project again 2", () => {
|
||||
@@ -4744,18 +4780,18 @@ namespace ts.projectSystem {
|
||||
service.openClientFile(f1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, projectDir);
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
assert.isDefined(service.configuredProjects.get(configFile.path));
|
||||
checkWatchedFiles(host, [fakes.FakeServerHost.libPath, configFile.path]);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, getTypeRootsFromLocation(configFileLocation).concat(configFileLocation), /*recursive*/ true);
|
||||
host.checkWatchedFiles([fakes.FakeServerHost.libPath, configFile.path]);
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
host.checkWatchedDirectories(getTypeRootsFromLocation(configFileLocation).concat(configFileLocation), /*recursive*/ true);
|
||||
|
||||
// Delete config file - should create inferred project with project root path set
|
||||
host.vfs.removeFile(configFile.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkNumberOfProjects(service, { inferredProjects: 1 });
|
||||
assert.equal(service.inferredProjects[0].projectRootPath, projectDir);
|
||||
checkWatchedFiles(host, [fakes.FakeServerHost.libPath, configFile.path, `${configFileLocation}/jsconfig.json`, `${projectDir}/tsconfig.json`, `${projectDir}/jsconfig.json`]);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, getTypeRootsFromLocation(projectDir), /*recursive*/ true);
|
||||
host.checkWatchedFiles([fakes.FakeServerHost.libPath, configFile.path, `${configFileLocation}/jsconfig.json`, `${projectDir}/tsconfig.json`, `${projectDir}/jsconfig.json`]);
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
host.checkWatchedDirectories(getTypeRootsFromLocation(projectDir), /*recursive*/ true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5305,7 +5341,7 @@ namespace ts.projectSystem {
|
||||
|
||||
function verifyCalledOnEachEntry(callback: CalledMaps, expectedKeys: Map<number>) {
|
||||
const calledMap = calledMaps[callback];
|
||||
ts.TestFSWithWatch.verifyMapSize(callback, calledMap, arrayFrom(expectedKeys.keys()));
|
||||
verifyMapSize(callback, calledMap, arrayFrom(expectedKeys.keys()));
|
||||
expectedKeys.forEach((called, name) => {
|
||||
assert.isTrue(calledMap.has(name), `${callback} is expected to contain ${name}, actual keys: ${arrayFrom(calledMap.keys())}`);
|
||||
assert.equal(calledMap.get(name).length, called, `${callback} is expected to be called ${called} times with ${name}. Actual entry: ${calledMap.get(name)}`);
|
||||
@@ -5676,9 +5712,9 @@ namespace ts.projectSystem {
|
||||
|
||||
function verifyProjectAndWatchedDirectories() {
|
||||
checkProjectActualFiles(project, map(projectFiles, f => f.path));
|
||||
checkWatchedFiles(host, mapDefined(projectFiles, getFilePathIfNotOpen));
|
||||
checkWatchedDirectories(host, watchingRecursiveDirectories, /*recursive*/ true);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
host.checkWatchedFiles(mapDefined(projectFiles, getFilePathIfNotOpen));
|
||||
host.checkWatchedDirectories(watchingRecursiveDirectories, /*recursive*/ true);
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5867,9 +5903,9 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(project, projectFilePaths);
|
||||
|
||||
const filesWatched = filter(projectFilePaths, p => p !== app.path);
|
||||
checkWatchedFiles(host, filesWatched);
|
||||
checkWatchedDirectories(host, typeRootDirectories.concat(recursiveWatchedDirectories), /*recursive*/ true);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
host.checkWatchedFiles(filesWatched);
|
||||
host.checkWatchedDirectories(typeRootDirectories.concat(recursiveWatchedDirectories), /*recursive*/ true);
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6398,8 +6434,8 @@ namespace ts.projectSystem {
|
||||
|
||||
function verifyProject() {
|
||||
checkProjectActualFiles(project, map(projectFiles, file => file.path));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
host.checkWatchedDirectories(watchedRecursiveDirectories, /*recursive*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6526,9 +6562,9 @@ namespace ts.projectSystem {
|
||||
const project = projectService.configuredProjects.get(configFile.path);
|
||||
assert.isDefined(project);
|
||||
checkProjectActualFiles(project, files.map(f => f.path));
|
||||
checkWatchedFiles(host, mapDefined(files, f => f === libFile ? windowsStyleLibFilePath : f === file1 ? undefined : f.path));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [
|
||||
host.checkWatchedFiles(mapDefined(files, f => f === libFile ? windowsStyleLibFilePath : f === file1 ? undefined : f.path));
|
||||
host.checkWatchedDirectories([], /*recursive*/ false);
|
||||
host.checkWatchedDirectories([
|
||||
root + "project",
|
||||
root + "project/node_modules/@types"
|
||||
].concat(useProjectAtRoot ? [] : [root + nodeModulesAtTypes]), /*recursive*/ true);
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace ts.projectSystem {
|
||||
import TI = server.typingsInstaller;
|
||||
import validatePackageName = JsTyping.validatePackageName;
|
||||
import PackageNameValidationResult = JsTyping.PackageNameValidationResult;
|
||||
import FileOrFolder = ts.TestFSWithWatch.FileOrFolder;
|
||||
|
||||
interface InstallerParams {
|
||||
globalTypingsCacheLocation?: string;
|
||||
@@ -45,15 +44,10 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}
|
||||
|
||||
function executeCommand(self: Installer, host: fakes.FakeServerHost, installedTypings: string[] | string, typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void {
|
||||
function executeCommand(self: Installer, host: fakes.FakeServerHost, installedTypings: string[] | string, typingFiles: { path: string, content: string }[], cb: TI.RequestCompletedAction): void {
|
||||
self.addPostExecAction(installedTypings, success => {
|
||||
for (const file of typingFiles) {
|
||||
if (typeof file.content === "string") {
|
||||
host.vfs.addFile(file.path, file.content, { overwrite: true });
|
||||
}
|
||||
else {
|
||||
host.vfs.addDirectory(file.path);
|
||||
}
|
||||
host.vfs.writeFile(file.path, file.content);
|
||||
}
|
||||
cb(success);
|
||||
});
|
||||
@@ -629,7 +623,7 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
const p = configuredProjectAt(projectService, 0);
|
||||
checkProjectActualFiles(p, [app.path, jsconfig.path]);
|
||||
ts.TestFSWithWatch.checkWatchedFiles(host, [jsconfig.path, "/bower_components", "/node_modules", "/.ts/lib.d.ts"]);
|
||||
host.checkWatchedFiles([jsconfig.path, "/bower_components", "/node_modules", "/.ts/lib.d.ts"]);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
|
||||
@@ -103,4 +103,11 @@ namespace utils {
|
||||
}
|
||||
return indentation;
|
||||
}
|
||||
|
||||
export function checkFileNames(caption: string, actualFileNames: ReadonlyArray<string>, 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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,791 +0,0 @@
|
||||
/// <reference path="harness.ts" />
|
||||
/// <reference path="./fakes.ts" />
|
||||
|
||||
// TODO(rbuckton): Migrate this to use vfs.
|
||||
|
||||
namespace ts.TestFSWithWatch {
|
||||
export const libFile: FileOrFolder = {
|
||||
path: "/.ts/lib.d.ts",
|
||||
content: `/// <reference no-default-lib="true"/>
|
||||
interface Boolean {}
|
||||
interface Function {}
|
||||
interface IArguments {}
|
||||
interface Number { toExponential: any; }
|
||||
interface Object {}
|
||||
interface RegExp {}
|
||||
interface String { charAt: any; }
|
||||
interface Array<T> {}`
|
||||
};
|
||||
|
||||
export const safeList = {
|
||||
path: <Path>"/safeList.json",
|
||||
content: JSON.stringify({
|
||||
commander: "commander",
|
||||
express: "express",
|
||||
jquery: "jquery",
|
||||
lodash: "lodash",
|
||||
moment: "moment",
|
||||
chroma: "chroma-js"
|
||||
})
|
||||
};
|
||||
|
||||
function getExecutingFilePathFromLibFile(): string {
|
||||
return combinePaths(getDirectoryPath(libFile.path), "tsc.js");
|
||||
}
|
||||
|
||||
interface TestServerHostCreationParameters {
|
||||
useCaseSensitiveFileNames?: boolean;
|
||||
executingFilePath?: string;
|
||||
currentDirectory?: string;
|
||||
newLine?: string;
|
||||
useWindowsStylePaths?: boolean;
|
||||
}
|
||||
|
||||
export function createServerHost(fileOrFolderList: ReadonlyArray<FileOrFolder>, params?: TestServerHostCreationParameters): TestServerHost {
|
||||
if (!params) {
|
||||
params = {};
|
||||
}
|
||||
const host = new TestServerHost(/*withSafelist*/ true,
|
||||
params.useCaseSensitiveFileNames !== undefined ? params.useCaseSensitiveFileNames : false,
|
||||
params.executingFilePath || getExecutingFilePathFromLibFile(),
|
||||
params.currentDirectory || "/",
|
||||
fileOrFolderList,
|
||||
params.newLine,
|
||||
params.useWindowsStylePaths);
|
||||
return host;
|
||||
}
|
||||
|
||||
export interface FileOrFolder {
|
||||
path: string;
|
||||
content?: string;
|
||||
fileSize?: number;
|
||||
}
|
||||
|
||||
interface FSEntry {
|
||||
path: Path;
|
||||
fullPath: string;
|
||||
}
|
||||
|
||||
interface File extends FSEntry {
|
||||
content: string;
|
||||
fileSize?: number;
|
||||
}
|
||||
|
||||
interface Folder extends FSEntry {
|
||||
entries: FSEntry[];
|
||||
}
|
||||
|
||||
function isFolder(s: FSEntry): s is Folder {
|
||||
return s && isArray((<Folder>s).entries);
|
||||
}
|
||||
|
||||
function isFile(s: FSEntry): s is File {
|
||||
return s && isString((<File>s).content);
|
||||
}
|
||||
|
||||
function invokeWatcherCallbacks<T>(callbacks: T[], invokeCallback: (cb: T) => void): void {
|
||||
if (callbacks) {
|
||||
// The array copy is made to ensure that even if one of the callback removes the callbacks,
|
||||
// we dont miss any callbacks following it
|
||||
const cbs = callbacks.slice();
|
||||
for (const cb of cbs) {
|
||||
invokeCallback(cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getDiffInKeys<T>(map: Map<T>, expectedKeys: ReadonlyArray<string>) {
|
||||
if (map.size === expectedKeys.length) {
|
||||
return "";
|
||||
}
|
||||
const notInActual: string[] = [];
|
||||
const duplicates: string[] = [];
|
||||
const seen = createMap<true>();
|
||||
forEach(expectedKeys, expectedKey => {
|
||||
if (seen.has(expectedKey)) {
|
||||
duplicates.push(expectedKey);
|
||||
return;
|
||||
}
|
||||
seen.set(expectedKey, true);
|
||||
if (!map.has(expectedKey)) {
|
||||
notInActual.push(expectedKey);
|
||||
}
|
||||
});
|
||||
const inActualNotExpected: string[] = [];
|
||||
map.forEach((_value, key) => {
|
||||
if (!seen.has(key)) {
|
||||
inActualNotExpected.push(key);
|
||||
}
|
||||
seen.set(key, true);
|
||||
});
|
||||
return `\n\nNotInActual: ${notInActual}\nDuplicates: ${duplicates}\nInActualButNotInExpected: ${inActualNotExpected}`;
|
||||
}
|
||||
|
||||
export function verifyMapSize(caption: string, map: Map<any>, expectedKeys: ReadonlyArray<string>) {
|
||||
assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map: Actual keys: ${arrayFrom(map.keys())} Expected: ${expectedKeys}${getDiffInKeys(map, expectedKeys)}`);
|
||||
}
|
||||
|
||||
function checkMapKeys(caption: string, map: Map<any>, expectedKeys: ReadonlyArray<string>) {
|
||||
verifyMapSize(caption, map, expectedKeys);
|
||||
for (const name of expectedKeys) {
|
||||
assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkFileNames(caption: string, actualFileNames: ReadonlyArray<string>, 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(contains(actualFileNames, f), `${caption}: expected to find ${f} in ${actualFileNames}`);
|
||||
}
|
||||
}
|
||||
|
||||
function checkSortedSet<T>(set: ReadonlySet<T>, values: ReadonlyArray<T>) {
|
||||
assert.strictEqual(set.size, values.length, `Actual: ${Array.from(set)}, expected: ${values}.`);
|
||||
for (const value of values) {
|
||||
assert.isTrue(set.has(value));
|
||||
}
|
||||
}
|
||||
|
||||
export function checkWatchedFiles(host: TestServerHost | fakes.FakeServerHost, expectedFiles: string[]) {
|
||||
if (host instanceof fakes.FakeServerHost) {
|
||||
return checkSortedSet(host.vfs.watchedFiles, expectedFiles);
|
||||
}
|
||||
|
||||
checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles);
|
||||
}
|
||||
|
||||
export function checkWatchedDirectories(host: TestServerHost | fakes.FakeServerHost, expectedDirectories: string[], recursive = false) {
|
||||
if (host instanceof fakes.FakeServerHost) {
|
||||
return checkSortedSet(recursive ? host.vfs.watchedRecursiveDirectories : host.vfs.watchedNonRecursiveDirectories, expectedDirectories);
|
||||
}
|
||||
checkMapKeys(`watchedDirectories${recursive ? " recursive" : ""}`, recursive ? host.watchedDirectoriesRecursive : host.watchedDirectories, expectedDirectories);
|
||||
}
|
||||
|
||||
export function checkOutputContains(host: TestServerHost | fakes.FakeServerHost, expected: ReadonlyArray<string>) {
|
||||
const mapExpected = arrayToSet(expected);
|
||||
const mapSeen = createMap<true>();
|
||||
for (const f of host.getOutput()) {
|
||||
assert.isUndefined(mapSeen.get(f), `Already found ${f} in ${JSON.stringify(host.getOutput())}`);
|
||||
if (mapExpected.has(f)) {
|
||||
mapExpected.delete(f);
|
||||
mapSeen.set(f, true);
|
||||
}
|
||||
}
|
||||
assert.equal(mapExpected.size, 0, `Output has missing ${JSON.stringify(flatMapIter(mapExpected.keys(), key => key))} in ${JSON.stringify(host.getOutput())}`);
|
||||
}
|
||||
|
||||
export function checkOutputDoesNotContain(host: TestServerHost | fakes.FakeServerHost, expectedToBeAbsent: string[] | ReadonlyArray<string>) {
|
||||
const mapExpectedToBeAbsent = arrayToSet(expectedToBeAbsent);
|
||||
for (const f of host.getOutput()) {
|
||||
assert.isFalse(mapExpectedToBeAbsent.has(f), `Contains ${f} in ${JSON.stringify(host.getOutput())}`);
|
||||
}
|
||||
}
|
||||
|
||||
class Callbacks {
|
||||
private map: TimeOutCallback[] = [];
|
||||
private nextId = 1;
|
||||
|
||||
getNextId() {
|
||||
return this.nextId;
|
||||
}
|
||||
|
||||
register(cb: (...args: any[]) => void, args: any[]) {
|
||||
const timeoutId = this.nextId;
|
||||
this.nextId++;
|
||||
this.map[timeoutId] = cb.bind(/*this*/ undefined, ...args);
|
||||
return timeoutId;
|
||||
}
|
||||
|
||||
unregister(id: any) {
|
||||
if (typeof id === "number") {
|
||||
delete this.map[id];
|
||||
}
|
||||
}
|
||||
|
||||
count() {
|
||||
let n = 0;
|
||||
for (const _ in this.map) {
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
invoke(invokeKey?: number) {
|
||||
if (invokeKey) {
|
||||
this.map[invokeKey]();
|
||||
delete this.map[invokeKey];
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: invoking a callback may result in new callbacks been queued,
|
||||
// so do not clear the entire callback list regardless. Only remove the
|
||||
// ones we have invoked.
|
||||
for (const key in this.map) {
|
||||
this.map[key]();
|
||||
delete this.map[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TimeOutCallback = () => any;
|
||||
|
||||
export interface TestFileWatcher {
|
||||
cb: FileWatcherCallback;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export interface TestDirectoryWatcher {
|
||||
cb: DirectoryWatcherCallback;
|
||||
directoryName: string;
|
||||
}
|
||||
|
||||
export interface ReloadWatchInvokeOptions {
|
||||
invokeDirectoryWatcherInsteadOfFileChanged: boolean;
|
||||
ignoreWatchInvokedWithTriggerAsFileCreate: boolean;
|
||||
}
|
||||
|
||||
function createFileMatcher(host: TestServerHost, path: string) {
|
||||
path = host.getCanonicalFileName(host.getHostSpecificPath(path));
|
||||
return (file: FileOrFolder) => host.getCanonicalFileName(host.getHostSpecificPath(file.path)) === path && isString(file.content);
|
||||
}
|
||||
|
||||
function createFolderMatcher(host: TestServerHost, path: string) {
|
||||
path = host.getCanonicalFileName(host.getHostSpecificPath(path));
|
||||
return (file: FileOrFolder) => host.getCanonicalFileName(host.getHostSpecificPath(file.path)) === path && !isString(file.content);
|
||||
}
|
||||
|
||||
export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost {
|
||||
args: string[] = [];
|
||||
|
||||
private readonly output: string[] = [];
|
||||
|
||||
private fs: Map<FSEntry> = createMap<FSEntry>();
|
||||
getCanonicalFileName: (s: string) => string;
|
||||
private toPath: (f: string) => Path;
|
||||
private timeoutCallbacks = new Callbacks();
|
||||
private immediateCallbacks = new Callbacks();
|
||||
|
||||
readonly watchedDirectories = createMultiMap<TestDirectoryWatcher>();
|
||||
readonly watchedDirectoriesRecursive = createMultiMap<TestDirectoryWatcher>();
|
||||
readonly watchedFiles = createMultiMap<TestFileWatcher>();
|
||||
private readonly executingFilePath: string;
|
||||
private readonly currentDirectory: string;
|
||||
public files: FileOrFolder[] = [];
|
||||
|
||||
// temporary vfs shim
|
||||
public vfs = {
|
||||
watchFiles: true,
|
||||
watchDirectories: true,
|
||||
addFile: (path: string, content: string, options?: { overwrite?: boolean }) => {
|
||||
let file = this.files.find(createFileMatcher(this, path));
|
||||
if (file) {
|
||||
if (!(options && options.overwrite)) return undefined;
|
||||
file.content = content;
|
||||
}
|
||||
else {
|
||||
file = { path, content };
|
||||
this.files.push(file);
|
||||
}
|
||||
this.reloadFS(this.files);
|
||||
return file;
|
||||
},
|
||||
writeFile: (path: string, content: string) => {
|
||||
let file = this.files.find(createFileMatcher(this, path));
|
||||
if (file) {
|
||||
file.content = content;
|
||||
}
|
||||
else {
|
||||
file = { path, content };
|
||||
this.files.push(file);
|
||||
}
|
||||
this.reloadFS(this.files);
|
||||
},
|
||||
getFile: (path: string) => {
|
||||
return this.files.find(createFileMatcher(this, path));
|
||||
},
|
||||
removeFile: (path: string) => {
|
||||
const index = this.files.findIndex(createFileMatcher(this, path));
|
||||
if (index >= 0) {
|
||||
ts.orderedRemoveItemAt(this.files, index);
|
||||
this.reloadFS(this.files);
|
||||
}
|
||||
},
|
||||
rename: (oldpath: string, newpath: string) => {
|
||||
if (this.vfs.getFile(oldpath)) {
|
||||
const oldIndex = this.files.findIndex(createFileMatcher(this, oldpath));
|
||||
if (oldIndex >= 0) {
|
||||
const oldItem = this.files[oldIndex];
|
||||
ts.orderedRemoveItemAt(this.files, oldIndex);
|
||||
const newIndex = this.files.findIndex(createFileMatcher(this, newpath));
|
||||
if (newIndex >= 0) {
|
||||
ts.orderedRemoveItemAt(this.files, newIndex);
|
||||
}
|
||||
this.files.push({ path: newpath, content: oldItem.content });
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < this.files.length; i++) {
|
||||
const file = this.files[i];
|
||||
if (vpath.beneath(oldpath, file.path, !this.useCaseSensitiveFileNames)) {
|
||||
this.files[i] = {
|
||||
path: vpath.combine(newpath, vpath.relative(oldpath, file.path, !this.useCaseSensitiveFileNames)),
|
||||
content: file.content
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
this.reloadFS(this.files);
|
||||
},
|
||||
addDirectory: (path: string) => {
|
||||
let file = this.files.find(createFolderMatcher(this, path));
|
||||
if (!file) {
|
||||
file = { path };
|
||||
this.files.push(file);
|
||||
this.reloadFS(this.files);
|
||||
}
|
||||
return file;
|
||||
},
|
||||
removeDirectory: (path: string) => {
|
||||
const index = this.files.findIndex(createFolderMatcher(this, path));
|
||||
if (index >= 0) {
|
||||
ts.orderedRemoveItemAt(this.files, index);
|
||||
this.reloadFS(this.files);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, executingFilePath: string, currentDirectory: string, fileOrFolderList: ReadonlyArray<FileOrFolder>, public readonly newLine = "\n", public readonly useWindowsStylePath?: boolean) {
|
||||
this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName);
|
||||
this.executingFilePath = this.getHostSpecificPath(executingFilePath);
|
||||
this.currentDirectory = this.getHostSpecificPath(currentDirectory);
|
||||
this.reloadFS(fileOrFolderList);
|
||||
}
|
||||
|
||||
getNewLine() {
|
||||
return this.newLine;
|
||||
}
|
||||
|
||||
toNormalizedAbsolutePath(s: string) {
|
||||
return getNormalizedAbsolutePath(s, this.currentDirectory);
|
||||
}
|
||||
|
||||
toFullPath(s: string) {
|
||||
return this.toPath(this.toNormalizedAbsolutePath(s));
|
||||
}
|
||||
|
||||
getHostSpecificPath(s: string) {
|
||||
if (this.useWindowsStylePath && s.startsWith(directorySeparator)) {
|
||||
return "c:/" + s.substring(1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
reloadFS(fileOrFolderList: ReadonlyArray<FileOrFolder>, options?: Partial<ReloadWatchInvokeOptions>) {
|
||||
const mapNewLeaves = createMap<true>();
|
||||
const isNewFs = this.fs.size === 0;
|
||||
if (this.files !== fileOrFolderList) {
|
||||
this.files = fileOrFolderList = fileOrFolderList.concat(this.withSafeList ? safeList : []);
|
||||
}
|
||||
const filesOrFoldersToLoad: ReadonlyArray<FileOrFolder> = !this.useWindowsStylePath ? fileOrFolderList :
|
||||
fileOrFolderList.map<FileOrFolder>(f => {
|
||||
const result = clone(f);
|
||||
result.path = this.getHostSpecificPath(f.path);
|
||||
return result;
|
||||
});
|
||||
for (const fileOrDirectory of filesOrFoldersToLoad) {
|
||||
const path = this.toFullPath(fileOrDirectory.path);
|
||||
mapNewLeaves.set(path, true);
|
||||
// If its a change
|
||||
const currentEntry = this.fs.get(path);
|
||||
if (currentEntry) {
|
||||
if (isFile(currentEntry)) {
|
||||
if (isString(fileOrDirectory.content)) {
|
||||
// Update file
|
||||
if (currentEntry.content !== fileOrDirectory.content) {
|
||||
currentEntry.content = fileOrDirectory.content;
|
||||
if (options && options.invokeDirectoryWatcherInsteadOfFileChanged) {
|
||||
this.invokeDirectoryWatcher(getDirectoryPath(currentEntry.fullPath), currentEntry.fullPath);
|
||||
}
|
||||
else {
|
||||
this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: Changing from file => folder
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Folder
|
||||
if (isString(fileOrDirectory.content)) {
|
||||
// TODO: Changing from folder => file
|
||||
}
|
||||
else {
|
||||
// Folder update: Nothing to do.
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.ensureFileOrFolder(fileOrDirectory, options && options.ignoreWatchInvokedWithTriggerAsFileCreate);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNewFs) {
|
||||
this.fs.forEach((fileOrDirectory, path) => {
|
||||
// If this entry is not from the new file or folder
|
||||
if (!mapNewLeaves.get(path)) {
|
||||
// Leaf entries that arent in new list => remove these
|
||||
if (isFile(fileOrDirectory) || isFolder(fileOrDirectory) && fileOrDirectory.entries.length === 0) {
|
||||
this.removeFileOrFolder(fileOrDirectory, folder => !mapNewLeaves.get(folder.path));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renameFolder(folderName: string, newFolderName: string) {
|
||||
const fullPath = getNormalizedAbsolutePath(folderName, this.currentDirectory);
|
||||
const path = this.toPath(fullPath);
|
||||
const folder = this.fs.get(path) as Folder;
|
||||
Debug.assert(!!folder);
|
||||
|
||||
// Only remove the folder
|
||||
this.removeFileOrFolder(folder, returnFalse, /*isRenaming*/ true);
|
||||
|
||||
// Add updated folder with new folder name
|
||||
const newFullPath = getNormalizedAbsolutePath(newFolderName, this.currentDirectory);
|
||||
const newFolder = this.toFolder(newFullPath);
|
||||
const newPath = newFolder.path;
|
||||
const basePath = getDirectoryPath(path);
|
||||
Debug.assert(basePath !== path);
|
||||
Debug.assert(basePath === getDirectoryPath(newPath));
|
||||
const baseFolder = this.fs.get(basePath) as Folder;
|
||||
this.addFileOrFolderInFolder(baseFolder, newFolder);
|
||||
|
||||
// Invoke watches for files in the folder as deleted (from old path)
|
||||
for (const entry of folder.entries) {
|
||||
Debug.assert(isFile(entry));
|
||||
this.fs.delete(entry.path);
|
||||
this.invokeFileWatcher(entry.fullPath, FileWatcherEventKind.Deleted);
|
||||
|
||||
entry.fullPath = combinePaths(newFullPath, getBaseFileName(entry.fullPath));
|
||||
entry.path = this.toPath(entry.fullPath);
|
||||
newFolder.entries.push(entry);
|
||||
this.fs.set(entry.path, entry);
|
||||
this.invokeFileWatcher(entry.fullPath, FileWatcherEventKind.Created);
|
||||
}
|
||||
}
|
||||
|
||||
ensureFileOrFolder(fileOrDirectory: FileOrFolder, ignoreWatchInvokedWithTriggerAsFileCreate?: boolean) {
|
||||
if (isString(fileOrDirectory.content)) {
|
||||
const file = this.toFile(fileOrDirectory);
|
||||
Debug.assert(!this.fs.get(file.path));
|
||||
const baseFolder = this.ensureFolder(getDirectoryPath(file.fullPath));
|
||||
this.addFileOrFolderInFolder(baseFolder, file, ignoreWatchInvokedWithTriggerAsFileCreate);
|
||||
}
|
||||
else {
|
||||
const fullPath = getNormalizedAbsolutePath(fileOrDirectory.path, this.currentDirectory);
|
||||
this.ensureFolder(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
private ensureFolder(fullPath: string): Folder {
|
||||
const path = this.toPath(fullPath);
|
||||
let folder = this.fs.get(path) as Folder;
|
||||
if (!folder) {
|
||||
folder = this.toFolder(fullPath);
|
||||
const baseFullPath = getDirectoryPath(fullPath);
|
||||
if (fullPath !== baseFullPath) {
|
||||
// Add folder in the base folder
|
||||
const baseFolder = this.ensureFolder(baseFullPath);
|
||||
this.addFileOrFolderInFolder(baseFolder, folder);
|
||||
}
|
||||
else {
|
||||
// root folder
|
||||
Debug.assert(this.fs.size === 0);
|
||||
this.fs.set(path, folder);
|
||||
}
|
||||
}
|
||||
Debug.assert(isFolder(folder));
|
||||
return folder;
|
||||
}
|
||||
|
||||
private addFileOrFolderInFolder(folder: Folder, fileOrDirectory: File | Folder, ignoreWatch?: boolean) {
|
||||
folder.entries.push(fileOrDirectory);
|
||||
this.fs.set(fileOrDirectory.path, fileOrDirectory);
|
||||
|
||||
if (ignoreWatch) {
|
||||
return;
|
||||
}
|
||||
if (isFile(fileOrDirectory)) {
|
||||
this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Created);
|
||||
}
|
||||
this.invokeDirectoryWatcher(folder.fullPath, fileOrDirectory.fullPath);
|
||||
}
|
||||
|
||||
private removeFileOrFolder(fileOrDirectory: File | Folder, isRemovableLeafFolder: (folder: Folder) => boolean, isRenaming?: boolean) {
|
||||
const basePath = getDirectoryPath(fileOrDirectory.path);
|
||||
const baseFolder = this.fs.get(basePath) as Folder;
|
||||
if (basePath !== fileOrDirectory.path) {
|
||||
Debug.assert(!!baseFolder);
|
||||
filterMutate(baseFolder.entries, entry => entry !== fileOrDirectory);
|
||||
}
|
||||
this.fs.delete(fileOrDirectory.path);
|
||||
|
||||
if (isFile(fileOrDirectory)) {
|
||||
this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted);
|
||||
}
|
||||
else {
|
||||
Debug.assert(fileOrDirectory.entries.length === 0 || isRenaming);
|
||||
if (this.vfs.watchDirectories) {
|
||||
const relativePath = this.getRelativePathToDirectory(fileOrDirectory.fullPath, fileOrDirectory.fullPath);
|
||||
// Invoke directory and recursive directory watcher for the folder
|
||||
// Here we arent invoking recursive directory watchers for the base folders
|
||||
// since that is something we would want to do for both file as well as folder we are deleting
|
||||
invokeWatcherCallbacks(this.watchedDirectories.get(fileOrDirectory.path), cb => this.directoryCallback(cb, relativePath));
|
||||
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(fileOrDirectory.path), cb => this.directoryCallback(cb, relativePath));
|
||||
}
|
||||
}
|
||||
|
||||
if (basePath !== fileOrDirectory.path) {
|
||||
if (baseFolder.entries.length === 0 && isRemovableLeafFolder(baseFolder)) {
|
||||
this.removeFileOrFolder(baseFolder, isRemovableLeafFolder);
|
||||
}
|
||||
else {
|
||||
this.invokeRecursiveDirectoryWatcher(baseFolder.fullPath, fileOrDirectory.fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind) {
|
||||
if (!this.vfs.watchFiles) return;
|
||||
const callbacks = this.watchedFiles.get(this.toPath(fileFullPath));
|
||||
invokeWatcherCallbacks(callbacks, ({ cb, fileName }) => cb(fileName, eventKind));
|
||||
}
|
||||
|
||||
private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) {
|
||||
return getRelativePathToDirectoryOrUrl(directoryFullPath, fileFullPath, this.currentDirectory, this.getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will call the directory watcher for the folderFullPath and recursive directory watchers for this and base folders
|
||||
*/
|
||||
private invokeDirectoryWatcher(folderFullPath: string, fileName: string) {
|
||||
if (!this.vfs.watchDirectories) return;
|
||||
const relativePath = this.getRelativePathToDirectory(folderFullPath, fileName);
|
||||
invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath)), cb => this.directoryCallback(cb, relativePath));
|
||||
this.invokeRecursiveDirectoryWatcher(folderFullPath, fileName);
|
||||
}
|
||||
|
||||
private directoryCallback({ cb, directoryName }: TestDirectoryWatcher, relativePath: string) {
|
||||
cb(combinePaths(directoryName, relativePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* This will call the recursive directory watcher for this directory as well as all the base directories
|
||||
*/
|
||||
private invokeRecursiveDirectoryWatcher(fullPath: string, fileName: string) {
|
||||
if (!this.vfs.watchDirectories) return;
|
||||
const relativePath = this.getRelativePathToDirectory(fullPath, fileName);
|
||||
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(this.toPath(fullPath)), cb => this.directoryCallback(cb, relativePath));
|
||||
const basePath = getDirectoryPath(fullPath);
|
||||
if (this.getCanonicalFileName(fullPath) !== this.getCanonicalFileName(basePath)) {
|
||||
this.invokeRecursiveDirectoryWatcher(basePath, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
private toFile(fileOrDirectory: FileOrFolder): File {
|
||||
const fullPath = getNormalizedAbsolutePath(fileOrDirectory.path, this.currentDirectory);
|
||||
return {
|
||||
path: this.toPath(fullPath),
|
||||
content: fileOrDirectory.content,
|
||||
fullPath,
|
||||
fileSize: fileOrDirectory.fileSize
|
||||
};
|
||||
}
|
||||
|
||||
private toFolder(path: string): Folder {
|
||||
const fullPath = getNormalizedAbsolutePath(path, this.currentDirectory);
|
||||
return {
|
||||
path: this.toPath(fullPath),
|
||||
entries: [],
|
||||
fullPath
|
||||
};
|
||||
}
|
||||
|
||||
fileExists(s: string) {
|
||||
const path = this.toFullPath(s);
|
||||
return isFile(this.fs.get(path));
|
||||
}
|
||||
|
||||
readFile(s: string) {
|
||||
const fsEntry = this.fs.get(this.toFullPath(s));
|
||||
return isFile(fsEntry) ? fsEntry.content : undefined;
|
||||
}
|
||||
|
||||
getFileSize(s: string) {
|
||||
const path = this.toFullPath(s);
|
||||
const entry = this.fs.get(path);
|
||||
if (isFile(entry)) {
|
||||
return entry.fileSize ? entry.fileSize : entry.content.length;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
directoryExists(s: string) {
|
||||
const path = this.toFullPath(s);
|
||||
return isFolder(this.fs.get(path));
|
||||
}
|
||||
|
||||
getDirectories(s: string) {
|
||||
const path = this.toFullPath(s);
|
||||
const folder = this.fs.get(path);
|
||||
if (isFolder(folder)) {
|
||||
return mapDefined(folder.entries, entry => isFolder(entry) ? getBaseFileName(entry.fullPath) : undefined);
|
||||
}
|
||||
Debug.fail(folder ? "getDirectories called on file" : "getDirectories called on missing folder");
|
||||
return [];
|
||||
}
|
||||
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
return ts.matchFiles(this.toNormalizedAbsolutePath(path), extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => {
|
||||
const directories: string[] = [];
|
||||
const files: string[] = [];
|
||||
const dirEntry = this.fs.get(this.toPath(dir));
|
||||
if (isFolder(dirEntry)) {
|
||||
dirEntry.entries.forEach((entry) => {
|
||||
if (isFolder(entry)) {
|
||||
directories.push(getBaseFileName(entry.fullPath));
|
||||
}
|
||||
else if (isFile(entry)) {
|
||||
files.push(getBaseFileName(entry.fullPath));
|
||||
}
|
||||
else {
|
||||
Debug.fail("Unknown entry");
|
||||
}
|
||||
});
|
||||
}
|
||||
return { directories, files };
|
||||
});
|
||||
}
|
||||
|
||||
watchDirectory(directoryName: string, cb: DirectoryWatcherCallback, recursive: boolean): FileWatcher {
|
||||
const path = this.toFullPath(directoryName);
|
||||
const map = recursive ? this.watchedDirectoriesRecursive : this.watchedDirectories;
|
||||
const callback: TestDirectoryWatcher = {
|
||||
cb,
|
||||
directoryName
|
||||
};
|
||||
map.add(path, callback);
|
||||
return {
|
||||
close: () => map.remove(path, callback)
|
||||
};
|
||||
}
|
||||
|
||||
createHash(s: string): string {
|
||||
return Harness.mockHash(s);
|
||||
}
|
||||
|
||||
watchFile(fileName: string, cb: FileWatcherCallback) {
|
||||
const path = this.toFullPath(fileName);
|
||||
const callback: TestFileWatcher = { fileName, cb };
|
||||
this.watchedFiles.add(path, callback);
|
||||
return { close: () => this.watchedFiles.remove(path, callback) };
|
||||
}
|
||||
|
||||
// TOOD: record and invoke callbacks to simulate timer events
|
||||
setTimeout(callback: TimeOutCallback, _time: number, ...args: any[]) {
|
||||
return this.timeoutCallbacks.register(callback, args);
|
||||
}
|
||||
|
||||
getNextTimeoutId() {
|
||||
return this.timeoutCallbacks.getNextId();
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId: any): void {
|
||||
this.timeoutCallbacks.unregister(timeoutId);
|
||||
}
|
||||
|
||||
checkTimeoutQueueLengthAndRun(expected: number) {
|
||||
this.checkTimeoutQueueLength(expected);
|
||||
this.runQueuedTimeoutCallbacks();
|
||||
}
|
||||
|
||||
checkTimeoutQueueLength(expected: number) {
|
||||
const callbacksCount = this.timeoutCallbacks.count();
|
||||
assert.equal(callbacksCount, expected, `expected ${expected} timeout callbacks queued but found ${callbacksCount}.`);
|
||||
}
|
||||
|
||||
runQueuedTimeoutCallbacks(timeoutId?: number) {
|
||||
try {
|
||||
this.timeoutCallbacks.invoke(timeoutId);
|
||||
}
|
||||
catch (e) {
|
||||
if (e.message === this.existMessage) {
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
runQueuedImmediateCallbacks() {
|
||||
this.immediateCallbacks.invoke();
|
||||
}
|
||||
|
||||
setImmediate(callback: TimeOutCallback, _time: number, ...args: any[]) {
|
||||
return this.immediateCallbacks.register(callback, args);
|
||||
}
|
||||
|
||||
clearImmediate(timeoutId: any): void {
|
||||
this.immediateCallbacks.unregister(timeoutId);
|
||||
}
|
||||
|
||||
createDirectory(directoryName: string): void {
|
||||
const folder = this.toFolder(directoryName);
|
||||
|
||||
// base folder has to be present
|
||||
const base = getDirectoryPath(folder.path);
|
||||
const baseFolder = this.fs.get(base) as Folder;
|
||||
Debug.assert(isFolder(baseFolder));
|
||||
|
||||
Debug.assert(!this.fs.get(folder.path));
|
||||
this.addFileOrFolderInFolder(baseFolder, folder);
|
||||
}
|
||||
|
||||
writeFile(path: string, content: string): void {
|
||||
const file = this.toFile({ path, content });
|
||||
|
||||
// base folder has to be present
|
||||
const base = getDirectoryPath(file.path);
|
||||
const folder = this.fs.get(base) as Folder;
|
||||
Debug.assert(isFolder(folder));
|
||||
|
||||
this.addFileOrFolderInFolder(folder, file);
|
||||
}
|
||||
|
||||
write(message: string) {
|
||||
this.output.push(message);
|
||||
}
|
||||
|
||||
getOutput(): ReadonlyArray<string> {
|
||||
return this.output;
|
||||
}
|
||||
|
||||
clearOutput() {
|
||||
clear(this.output);
|
||||
}
|
||||
|
||||
readonly existMessage = "System Exit";
|
||||
exitCode: number;
|
||||
readonly resolvePath = (s: string) => s;
|
||||
readonly getExecutingFilePath = () => this.executingFilePath;
|
||||
readonly getCurrentDirectory = () => this.currentDirectory;
|
||||
exit(exitCode?: number) {
|
||||
this.exitCode = exitCode;
|
||||
throw new Error(this.existMessage);
|
||||
}
|
||||
readonly getEnvironmentVariable = notImplemented;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -537,6 +537,10 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void;
|
||||
/** @internal */
|
||||
// tslint:disable-next-line:unified-signatures
|
||||
updateTypingsForProject(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes): void;
|
||||
updateTypingsForProject(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes): void {
|
||||
const project = this.findProject(response.projectName);
|
||||
if (!project) {
|
||||
|
||||
Reference in new issue
Block a user