mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Move the tests into their own folder for easy scenario search
This commit is contained in:
1 parent
9e17a66151
commit
5c8ef3934d
46 files changed
+1493
-1489
No files matched your search
@@ -36,74 +36,79 @@
|
||||
|
||||
"runner.ts",
|
||||
|
||||
"unittests/extractTestHelpers.ts",
|
||||
"unittests/tscWatchHelpers.ts",
|
||||
"unittests/tsserverHelpers.ts",
|
||||
"unittests/services/extract/helpers.ts",
|
||||
"unittests/tscWatch/helpers.ts",
|
||||
"unittests/tsserver/helpers.ts",
|
||||
|
||||
"unittests/asserts.ts",
|
||||
"unittests/base64.ts",
|
||||
"unittests/builder.ts",
|
||||
"unittests/cancellableLanguageServiceOperations.ts",
|
||||
"unittests/commandLineParsing.ts",
|
||||
"unittests/compileOnSave.ts",
|
||||
"unittests/compilerCore.ts",
|
||||
"unittests/configurationExtension.ts",
|
||||
"unittests/convertCompilerOptionsFromJson.ts",
|
||||
"unittests/convertToAsyncFunction.ts",
|
||||
"unittests/convertToBase64.ts",
|
||||
"unittests/convertTypeAcquisitionFromJson.ts",
|
||||
"unittests/customTransforms.ts",
|
||||
"unittests/extractConstants.ts",
|
||||
"unittests/extractFunctions.ts",
|
||||
"unittests/extractRanges.ts",
|
||||
"unittests/factory.ts",
|
||||
"unittests/hostNewLineSupport.ts",
|
||||
"unittests/incrementalParser.ts",
|
||||
"unittests/initializeTSConfig.ts",
|
||||
"unittests/jsDocParsing.ts",
|
||||
"unittests/languageService.ts",
|
||||
"unittests/matchFiles.ts",
|
||||
"unittests/moduleResolution.ts",
|
||||
"unittests/organizeImports.ts",
|
||||
"unittests/parsePseudoBigInt.ts",
|
||||
"unittests/paths.ts",
|
||||
"unittests/printer.ts",
|
||||
"unittests/programApi.ts",
|
||||
"unittests/projectErrors.ts",
|
||||
"unittests/projectReferences.ts",
|
||||
"unittests/publicApi.ts",
|
||||
"unittests/resolutionCache.ts",
|
||||
"unittests/reuseProgramStructure.ts",
|
||||
"unittests/session.ts",
|
||||
"unittests/semver.ts",
|
||||
"unittests/showConfig.ts",
|
||||
"unittests/symbolWalker.ts",
|
||||
"unittests/telemetry.ts",
|
||||
"unittests/textChanges.ts",
|
||||
"unittests/textStorage.ts",
|
||||
"unittests/transform.ts",
|
||||
"unittests/transpile.ts",
|
||||
"unittests/tsbuild.ts",
|
||||
"unittests/tsbuildWatchMode.ts",
|
||||
"unittests/tsconfigParsing.ts",
|
||||
"unittests/tscWatchEmit.ts",
|
||||
"unittests/tscWatchMode.ts",
|
||||
"unittests/tsserverCachingFileSystemInformation.ts",
|
||||
"unittests/tsserverLargeFileReferencedEvent.ts",
|
||||
"unittests/tsserverProjectLoadingEvents.ts",
|
||||
"unittests/tsserverProjectSystem.ts",
|
||||
"unittests/tsserverProjectUpdatedInBackgroundEvent.ts",
|
||||
"unittests/tsserverSymLinks.ts",
|
||||
"unittests/typingsInstaller.ts",
|
||||
"unittests/versionCache.ts",
|
||||
"unittests/watchEnvironment.ts",
|
||||
"unittests/watchApi.ts",
|
||||
"unittests/config/commandLineParsing.ts",
|
||||
"unittests/config/configurationExtension.ts",
|
||||
"unittests/config/convertCompilerOptionsFromJson.ts",
|
||||
"unittests/config/convertTypeAcquisitionFromJson.ts",
|
||||
"unittests/config/initializeTSConfig.ts",
|
||||
"unittests/config/matchFiles.ts",
|
||||
"unittests/config/projectReferences.ts",
|
||||
"unittests/config/showConfig.ts",
|
||||
"unittests/config/tsconfigParsing.ts",
|
||||
"unittests/evaluation/asyncArrow.ts",
|
||||
"unittests/evaluation/asyncGenerator.ts",
|
||||
"unittests/evaluation/forAwaitOf.ts",
|
||||
"unittests/services/cancellableLanguageServiceOperations.ts",
|
||||
"unittests/services/colorization.ts",
|
||||
"unittests/services/convertToAsyncFunction.ts",
|
||||
"unittests/services/documentRegistry.ts",
|
||||
"unittests/services/extract/constants.ts",
|
||||
"unittests/services/extract/functions.ts",
|
||||
"unittests/services/extract/symbolWalker.ts",
|
||||
"unittests/services/extract/ranges.ts",
|
||||
"unittests/services/hostNewLineSupport.ts",
|
||||
"unittests/services/languageService.ts",
|
||||
"unittests/services/organizeImports.ts",
|
||||
"unittests/services/patternMatcher.ts",
|
||||
"unittests/services/preProcessFile.ts"
|
||||
"unittests/services/preProcessFile.ts",
|
||||
"unittests/services/textChanges.ts",
|
||||
"unittests/tscWatch/emit.ts",
|
||||
"unittests/tscWatch/resolutionCache.ts",
|
||||
"unittests/tscWatch/watchEnvironment.ts",
|
||||
"unittests/tscWatch/watchApi.ts",
|
||||
"unittests/tsserver/cachingFileSystemInformation.ts",
|
||||
"unittests/tsserver/compileOnSave.ts",
|
||||
"unittests/tsserver/events/largeFileReferenced.ts",
|
||||
"unittests/tsserver/events/projectLoading.ts",
|
||||
"unittests/tsserver/events/projectUpdatedInBackground.ts",
|
||||
"unittests/tsserver/externalProjects.ts",
|
||||
"unittests/tsserver/projectErrors.ts",
|
||||
"unittests/tsserver/reload.ts",
|
||||
"unittests/tsserver/resolutionCache.ts",
|
||||
"unittests/tsserver/session.ts",
|
||||
"unittests/tsserver/skipLibCheck.ts",
|
||||
"unittests/tsserver/symLinks.ts",
|
||||
"unittests/tsserver/textStorage.ts",
|
||||
"unittests/tsserver/telemetry.ts",
|
||||
"unittests/tsserver/typingsInstaller.ts",
|
||||
"unittests/tsserver/versionCache.ts",
|
||||
"unittests/tsserver/watchEnvironment.ts"
|
||||
]
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -42,4 +42,4 @@ export default function foo(a: number, b: Bar): void {}`, (file, checker) => {
|
||||
assert.equal(stdLibRefSymbols, 1); // Expect 1 stdlib entry symbol - the implicit Array referenced by Bar.history
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,448 @@
|
||||
namespace ts.tscWatch {
|
||||
describe("resolutionCache:: tsc-watch module resolution caching", () => {
|
||||
it("works", () => {
|
||||
const root = {
|
||||
path: "/a/d/f0.ts",
|
||||
content: `import {x} from "f1"`
|
||||
};
|
||||
const imported = {
|
||||
path: "/a/f1.ts",
|
||||
content: `foo()`
|
||||
};
|
||||
|
||||
const files = [root, imported, libFile];
|
||||
const host = createWatchedSystem(files);
|
||||
const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD });
|
||||
|
||||
const f1IsNotModule = getDiagnosticOfFileFromProgram(watch(), root.path, root.content.indexOf('"f1"'), '"f1"'.length, Diagnostics.File_0_is_not_a_module, imported.path);
|
||||
const cannotFindFoo = getDiagnosticOfFileFromProgram(watch(), imported.path, imported.content.indexOf("foo"), "foo".length, Diagnostics.Cannot_find_name_0, "foo");
|
||||
|
||||
// ensure that imported file was found
|
||||
checkOutputErrorsInitial(host, [f1IsNotModule, cannotFindFoo]);
|
||||
|
||||
const originalFileExists = host.fileExists;
|
||||
{
|
||||
const newContent = `import {x} from "f1"
|
||||
var x: string = 1;`;
|
||||
root.content = newContent;
|
||||
host.reloadFS(files);
|
||||
|
||||
// patch fileExists to make sure that disk is not touched
|
||||
host.fileExists = notImplemented;
|
||||
|
||||
// trigger synchronization to make sure that import will be fetched from the cache
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
// ensure file has correct number of errors after edit
|
||||
checkOutputErrorsIncremental(host, [
|
||||
f1IsNotModule,
|
||||
getDiagnosticOfFileFromProgram(watch(), root.path, newContent.indexOf("var x") + "var ".length, "x".length, Diagnostics.Type_0_is_not_assignable_to_type_1, 1, "string"),
|
||||
cannotFindFoo
|
||||
]);
|
||||
}
|
||||
{
|
||||
let fileExistsIsCalled = false;
|
||||
host.fileExists = (fileName): boolean => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
fileExistsIsCalled = true;
|
||||
assert.isTrue(fileName.indexOf("/f2.") !== -1);
|
||||
return originalFileExists.call(host, fileName);
|
||||
};
|
||||
|
||||
root.content = `import {x} from "f2"`;
|
||||
host.reloadFS(files);
|
||||
|
||||
// trigger synchronization to make sure that LSHost will try to find 'f2' module on disk
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
// ensure file has correct number of errors after edit
|
||||
checkOutputErrorsIncremental(host, [
|
||||
getDiagnosticModuleNotFoundOfFile(watch(), root, "f2")
|
||||
]);
|
||||
|
||||
assert.isTrue(fileExistsIsCalled);
|
||||
}
|
||||
{
|
||||
let fileExistsCalled = false;
|
||||
host.fileExists = (fileName): boolean => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
fileExistsCalled = true;
|
||||
assert.isTrue(fileName.indexOf("/f1.") !== -1);
|
||||
return originalFileExists.call(host, fileName);
|
||||
};
|
||||
|
||||
const newContent = `import {x} from "f1"`;
|
||||
root.content = newContent;
|
||||
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
checkOutputErrorsIncremental(host, [f1IsNotModule, cannotFindFoo]);
|
||||
assert.isTrue(fileExistsCalled);
|
||||
}
|
||||
});
|
||||
|
||||
it("loads missing files from disk", () => {
|
||||
const root = {
|
||||
path: `/a/foo.ts`,
|
||||
content: `import {x} from "bar"`
|
||||
};
|
||||
|
||||
const imported = {
|
||||
path: `/a/bar.d.ts`,
|
||||
content: `export const y = 1;`
|
||||
};
|
||||
|
||||
const files = [root, libFile];
|
||||
const host = createWatchedSystem(files);
|
||||
const originalFileExists = host.fileExists;
|
||||
|
||||
let fileExistsCalledForBar = false;
|
||||
host.fileExists = fileName => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
if (!fileExistsCalledForBar) {
|
||||
fileExistsCalledForBar = fileName.indexOf("/bar.") !== -1;
|
||||
}
|
||||
|
||||
return originalFileExists.call(host, fileName);
|
||||
};
|
||||
|
||||
const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD });
|
||||
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
|
||||
checkOutputErrorsInitial(host, [
|
||||
getDiagnosticModuleNotFoundOfFile(watch(), root, "bar")
|
||||
]);
|
||||
|
||||
fileExistsCalledForBar = false;
|
||||
root.content = `import {y} from "bar"`;
|
||||
host.reloadFS(files.concat(imported));
|
||||
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
|
||||
});
|
||||
|
||||
it("should compile correctly when resolved module goes missing and then comes back (module is not part of the root)", () => {
|
||||
const root = {
|
||||
path: `/a/foo.ts`,
|
||||
content: `import {x} from "bar"`
|
||||
};
|
||||
|
||||
const imported = {
|
||||
path: `/a/bar.d.ts`,
|
||||
content: `export const y = 1;export const x = 10;`
|
||||
};
|
||||
|
||||
const files = [root, libFile];
|
||||
const filesWithImported = files.concat(imported);
|
||||
const host = createWatchedSystem(filesWithImported);
|
||||
const originalFileExists = host.fileExists;
|
||||
let fileExistsCalledForBar = false;
|
||||
host.fileExists = fileName => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
if (!fileExistsCalledForBar) {
|
||||
fileExistsCalledForBar = fileName.indexOf("/bar.") !== -1;
|
||||
}
|
||||
return originalFileExists.call(host, fileName);
|
||||
};
|
||||
|
||||
const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD });
|
||||
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
|
||||
fileExistsCalledForBar = false;
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
|
||||
checkOutputErrorsIncremental(host, [
|
||||
getDiagnosticModuleNotFoundOfFile(watch(), root, "bar")
|
||||
]);
|
||||
|
||||
fileExistsCalledForBar = false;
|
||||
host.reloadFS(filesWithImported);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
|
||||
});
|
||||
|
||||
it("works when module resolution changes to ambient module", () => {
|
||||
const root = {
|
||||
path: "/a/b/foo.ts",
|
||||
content: `import * as fs from "fs";`
|
||||
};
|
||||
|
||||
const packageJson = {
|
||||
path: "/a/b/node_modules/@types/node/package.json",
|
||||
content: `
|
||||
{
|
||||
"main": ""
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
const nodeType = {
|
||||
path: "/a/b/node_modules/@types/node/index.d.ts",
|
||||
content: `
|
||||
declare module "fs" {
|
||||
export interface Stats {
|
||||
isFile(): boolean;
|
||||
}
|
||||
}`
|
||||
};
|
||||
|
||||
const files = [root, libFile];
|
||||
const filesWithNodeType = files.concat(packageJson, nodeType);
|
||||
const host = createWatchedSystem(files, { currentDirectory: "/a/b" });
|
||||
|
||||
const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { });
|
||||
|
||||
checkOutputErrorsInitial(host, [
|
||||
getDiagnosticModuleNotFoundOfFile(watch(), root, "fs")
|
||||
]);
|
||||
|
||||
host.reloadFS(filesWithNodeType);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
});
|
||||
|
||||
it("works when included file with ambient module changes", () => {
|
||||
const root = {
|
||||
path: "/a/b/foo.ts",
|
||||
content: `
|
||||
import * as fs from "fs";
|
||||
import * as u from "url";
|
||||
`
|
||||
};
|
||||
|
||||
const file = {
|
||||
path: "/a/b/bar.d.ts",
|
||||
content: `
|
||||
declare module "url" {
|
||||
export interface Url {
|
||||
href?: string;
|
||||
}
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
const fileContentWithFS = `
|
||||
declare module "fs" {
|
||||
export interface Stats {
|
||||
isFile(): boolean;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const files = [root, file, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory: "/a/b" });
|
||||
|
||||
const watch = createWatchOfFilesAndCompilerOptions([root.path, file.path], host, {});
|
||||
|
||||
checkOutputErrorsInitial(host, [
|
||||
getDiagnosticModuleNotFoundOfFile(watch(), root, "fs")
|
||||
]);
|
||||
|
||||
file.content += fileContentWithFS;
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
});
|
||||
|
||||
it("works when reusing program with files from external library", () => {
|
||||
interface ExpectedFile { path: string; isExpectedToEmit?: boolean; content?: string; }
|
||||
const configDir = "/a/b/projects/myProject/src/";
|
||||
const file1: File = {
|
||||
path: configDir + "file1.ts",
|
||||
content: 'import module1 = require("module1");\nmodule1("hello");'
|
||||
};
|
||||
const file2: File = {
|
||||
path: configDir + "file2.ts",
|
||||
content: 'import module11 = require("module1");\nmodule11("hello");'
|
||||
};
|
||||
const module1: File = {
|
||||
path: "/a/b/projects/myProject/node_modules/module1/index.js",
|
||||
content: "module.exports = options => { return options.toString(); }"
|
||||
};
|
||||
const configFile: File = {
|
||||
path: configDir + "tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
allowJs: true,
|
||||
rootDir: ".",
|
||||
outDir: "../dist",
|
||||
moduleResolution: "node",
|
||||
maxNodeModuleJsDepth: 1
|
||||
}
|
||||
})
|
||||
};
|
||||
const outDirFolder = "/a/b/projects/myProject/dist/";
|
||||
const programFiles = [file1, file2, module1, libFile];
|
||||
const host = createWatchedSystem(programFiles.concat(configFile), { currentDirectory: "/a/b/projects/myProject/" });
|
||||
const watch = createWatchOfConfigFile(configFile.path, host);
|
||||
checkProgramActualFiles(watch(), programFiles.map(f => f.path));
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
const expectedFiles: ExpectedFile[] = [
|
||||
createExpectedEmittedFile(file1),
|
||||
createExpectedEmittedFile(file2),
|
||||
createExpectedToNotEmitFile("index.js"),
|
||||
createExpectedToNotEmitFile("src/index.js"),
|
||||
createExpectedToNotEmitFile("src/file1.js"),
|
||||
createExpectedToNotEmitFile("src/file2.js"),
|
||||
createExpectedToNotEmitFile("lib.js"),
|
||||
createExpectedToNotEmitFile("lib.d.ts")
|
||||
];
|
||||
verifyExpectedFiles(expectedFiles);
|
||||
|
||||
file1.content += "\n;";
|
||||
expectedFiles[0].content += ";\n"; // Only emit file1 with this change
|
||||
expectedFiles[1].isExpectedToEmit = false;
|
||||
host.reloadFS(programFiles.concat(configFile));
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkProgramActualFiles(watch(), programFiles.map(f => f.path));
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
verifyExpectedFiles(expectedFiles);
|
||||
|
||||
|
||||
function verifyExpectedFiles(expectedFiles: ExpectedFile[]) {
|
||||
forEach(expectedFiles, f => {
|
||||
assert.equal(!!host.fileExists(f.path), f.isExpectedToEmit, "File " + f.path + " is expected to " + (f.isExpectedToEmit ? "emit" : "not emit"));
|
||||
if (f.isExpectedToEmit) {
|
||||
assert.equal(host.readFile(f.path), f.content, "Expected contents of " + f.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createExpectedToNotEmitFile(fileName: string): ExpectedFile {
|
||||
return {
|
||||
path: outDirFolder + fileName,
|
||||
isExpectedToEmit: false
|
||||
};
|
||||
}
|
||||
|
||||
function createExpectedEmittedFile(file: File): ExpectedFile {
|
||||
return {
|
||||
path: removeFileExtension(file.path.replace(configDir, outDirFolder)) + Extension.Js,
|
||||
isExpectedToEmit: true,
|
||||
content: '"use strict";\nexports.__esModule = true;\n' + file.content.replace("import", "var") + "\n"
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
it("works when renaming node_modules folder that already contains @types folder", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const file: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
content: `import * as q from "qqq";`
|
||||
};
|
||||
const module: File = {
|
||||
path: `${currentDirectory}/node_modules2/@types/qqq/index.d.ts`,
|
||||
content: "export {}"
|
||||
};
|
||||
const files = [file, module, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const watch = createWatchOfFilesAndCompilerOptions([file.path], host);
|
||||
|
||||
checkProgramActualFiles(watch(), [file.path, libFile.path]);
|
||||
checkOutputErrorsInitial(host, [getDiagnosticModuleNotFoundOfFile(watch(), file, "qqq")]);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [`${currentDirectory}/node_modules`, `${currentDirectory}/node_modules/@types`], /*recursive*/ true);
|
||||
|
||||
host.renameFolder(`${currentDirectory}/node_modules2`, `${currentDirectory}/node_modules`);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkProgramActualFiles(watch(), [file.path, libFile.path, `${currentDirectory}/node_modules/@types/qqq/index.d.ts`]);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
});
|
||||
|
||||
describe("ignores files/folder changes in node_modules that start with '.'", () => {
|
||||
const projectPath = "/user/username/projects/project";
|
||||
const npmCacheFile: File = {
|
||||
path: `${projectPath}/node_modules/.cache/babel-loader/89c02171edab901b9926470ba6d5677e.ts`,
|
||||
content: JSON.stringify({ something: 10 })
|
||||
};
|
||||
const file1: File = {
|
||||
path: `${projectPath}/test.ts`,
|
||||
content: `import { x } from "somemodule";`
|
||||
};
|
||||
const file2: File = {
|
||||
path: `${projectPath}/node_modules/somemodule/index.d.ts`,
|
||||
content: `export const x = 10;`
|
||||
};
|
||||
const files = [libFile, file1, file2];
|
||||
const expectedFiles = files.map(f => f.path);
|
||||
it("when watching node_modules in inferred project for failed lookup", () => {
|
||||
const host = createWatchedSystem(files);
|
||||
const watch = createWatchOfFilesAndCompilerOptions([file1.path], host, {}, /*maxNumberOfFilesToIterateForInvalidation*/ 1);
|
||||
checkProgramActualFiles(watch(), expectedFiles);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
|
||||
host.ensureFileOrFolder(npmCacheFile);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
});
|
||||
it("when watching node_modules as part of wild card directories in config project", () => {
|
||||
const config: File = {
|
||||
path: `${projectPath}/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const host = createWatchedSystem(files.concat(config));
|
||||
const watch = createWatchOfConfigFile(config.path, host);
|
||||
checkProgramActualFiles(watch(), expectedFiles);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
|
||||
host.ensureFileOrFolder(npmCacheFile);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolutionCache:: tsc-watch with modules linked to sibling folder", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
const mainPackageRoot = `${projectRoot}/main`;
|
||||
const linkedPackageRoot = `${projectRoot}/linked-package`;
|
||||
const mainFile: File = {
|
||||
path: `${mainPackageRoot}/index.ts`,
|
||||
content: "import { Foo } from '@scoped/linked-package'"
|
||||
};
|
||||
const config: File = {
|
||||
path: `${mainPackageRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { module: "commonjs", moduleResolution: "node", baseUrl: ".", rootDir: "." },
|
||||
files: ["index.ts"]
|
||||
})
|
||||
};
|
||||
const linkedPackageInMain: SymLink = {
|
||||
path: `${mainPackageRoot}/node_modules/@scoped/linked-package`,
|
||||
symLink: `${linkedPackageRoot}`
|
||||
};
|
||||
const linkedPackageJson: File = {
|
||||
path: `${linkedPackageRoot}/package.json`,
|
||||
content: JSON.stringify({ name: "@scoped/linked-package", version: "0.0.1", types: "dist/index.d.ts", main: "dist/index.js" })
|
||||
};
|
||||
const linkedPackageIndex: File = {
|
||||
path: `${linkedPackageRoot}/dist/index.d.ts`,
|
||||
content: "export * from './other';"
|
||||
};
|
||||
const linkedPackageOther: File = {
|
||||
path: `${linkedPackageRoot}/dist/other.d.ts`,
|
||||
content: 'export declare const Foo = "BAR";'
|
||||
};
|
||||
|
||||
it("verify watched directories", () => {
|
||||
const files = [libFile, mainFile, config, linkedPackageInMain, linkedPackageJson, linkedPackageIndex, linkedPackageOther];
|
||||
const host = createWatchedSystem(files, { currentDirectory: mainPackageRoot });
|
||||
createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, mainFile.path, config.path, linkedPackageIndex.path, linkedPackageOther.path], 1);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [`${mainPackageRoot}/@scoped`, `${mainPackageRoot}/node_modules`, linkedPackageRoot, `${mainPackageRoot}/node_modules/@types`, `${projectRoot}/node_modules/@types`], 1, /*recursive*/ true);
|
||||
});
|
||||
});
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,178 @@
|
||||
namespace ts.tscWatch {
|
||||
import Tsc_WatchDirectory = TestFSWithWatch.Tsc_WatchDirectory;
|
||||
describe("watchEnvironment:: tsc-watch with different polling/non polling options", () => {
|
||||
it("watchFile using dynamic priority polling", () => {
|
||||
const projectFolder = "/a/username/project";
|
||||
const file1: File = {
|
||||
path: `${projectFolder}/typescript.ts`,
|
||||
content: "var z = 10;"
|
||||
};
|
||||
const files = [file1, libFile];
|
||||
const environmentVariables = createMap<string>();
|
||||
environmentVariables.set("TSC_WATCHFILE", "DynamicPriorityPolling");
|
||||
const host = createWatchedSystem(files, { environmentVariables });
|
||||
const watch = createWatchOfFilesAndCompilerOptions([file1.path], host);
|
||||
|
||||
const initialProgram = watch();
|
||||
verifyProgram();
|
||||
|
||||
const mediumPollingIntervalThreshold = unchangedPollThresholds[PollingInterval.Medium];
|
||||
for (let index = 0; index < mediumPollingIntervalThreshold; index++) {
|
||||
// Transition libFile and file1 to low priority queue
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
assert.deepEqual(watch(), initialProgram);
|
||||
}
|
||||
|
||||
// Make a change to file
|
||||
file1.content = "var zz30 = 100;";
|
||||
host.reloadFS(files);
|
||||
|
||||
// This should detect change in the file
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
assert.deepEqual(watch(), initialProgram);
|
||||
|
||||
// Callbacks: medium priority + high priority queue and scheduled program update
|
||||
host.checkTimeoutQueueLengthAndRun(3);
|
||||
// During this timeout the file would be detected as unchanged
|
||||
let fileUnchangeDetected = 1;
|
||||
const newProgram = watch();
|
||||
assert.notStrictEqual(newProgram, initialProgram);
|
||||
|
||||
verifyProgram();
|
||||
const outputFile1 = changeExtension(file1.path, ".js");
|
||||
assert.isTrue(host.fileExists(outputFile1));
|
||||
assert.equal(host.readFile(outputFile1), file1.content + host.newLine);
|
||||
|
||||
const newThreshold = unchangedPollThresholds[PollingInterval.Low] + mediumPollingIntervalThreshold;
|
||||
for (; fileUnchangeDetected < newThreshold; fileUnchangeDetected++) {
|
||||
// For high + Medium/low polling interval
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
assert.deepEqual(watch(), newProgram);
|
||||
}
|
||||
|
||||
// Everything goes in high polling interval queue
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
assert.deepEqual(watch(), newProgram);
|
||||
|
||||
function verifyProgram() {
|
||||
checkProgramActualFiles(watch(), files.map(f => f.path));
|
||||
checkWatchedFiles(host, []);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ true);
|
||||
}
|
||||
});
|
||||
|
||||
describe("tsc-watch when watchDirectories implementation", () => {
|
||||
function verifyRenamingFileInSubFolder(tscWatchDirectory: Tsc_WatchDirectory) {
|
||||
const projectFolder = "/a/username/project";
|
||||
const projectSrcFolder = `${projectFolder}/src`;
|
||||
const configFile: File = {
|
||||
path: `${projectFolder}/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const file: File = {
|
||||
path: `${projectSrcFolder}/file1.ts`,
|
||||
content: ""
|
||||
};
|
||||
const programFiles = [file, libFile];
|
||||
const files = [file, configFile, libFile];
|
||||
const environmentVariables = createMap<string>();
|
||||
environmentVariables.set("TSC_WATCHDIRECTORY", tscWatchDirectory);
|
||||
const host = createWatchedSystem(files, { environmentVariables });
|
||||
const watch = createWatchOfConfigFile(configFile.path, host);
|
||||
const projectFolders = [projectFolder, projectSrcFolder, `${projectFolder}/node_modules/@types`];
|
||||
// Watching files config file, file, lib file
|
||||
const expectedWatchedFiles = files.map(f => f.path);
|
||||
const expectedWatchedDirectories = tscWatchDirectory === Tsc_WatchDirectory.NonRecursiveWatchDirectory ? projectFolders : emptyArray;
|
||||
if (tscWatchDirectory === Tsc_WatchDirectory.WatchFile) {
|
||||
expectedWatchedFiles.push(...projectFolders);
|
||||
}
|
||||
|
||||
verifyProgram(checkOutputErrorsInitial);
|
||||
|
||||
// Rename the file:
|
||||
file.path = file.path.replace("file1.ts", "file2.ts");
|
||||
expectedWatchedFiles[0] = file.path;
|
||||
host.reloadFS(files);
|
||||
if (tscWatchDirectory === Tsc_WatchDirectory.DynamicPolling) {
|
||||
// With dynamic polling the fs change would be detected only by running timeouts
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
}
|
||||
// Delayed update program
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
verifyProgram(checkOutputErrorsIncremental);
|
||||
|
||||
function verifyProgram(checkOutputErrors: (host: WatchedSystem, errors: ReadonlyArray<Diagnostic>) => void) {
|
||||
checkProgramActualFiles(watch(), programFiles.map(f => f.path));
|
||||
checkOutputErrors(host, emptyArray);
|
||||
|
||||
const outputFile = changeExtension(file.path, ".js");
|
||||
assert(host.fileExists(outputFile));
|
||||
assert.equal(host.readFile(outputFile), file.content);
|
||||
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
|
||||
|
||||
// Watching config file, file, lib file and directories
|
||||
checkWatchedFilesDetailed(host, expectedWatchedFiles, 1);
|
||||
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, 1, /*recursive*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
it("uses watchFile when renaming file in subfolder", () => {
|
||||
verifyRenamingFileInSubFolder(Tsc_WatchDirectory.WatchFile);
|
||||
});
|
||||
|
||||
it("uses non recursive watchDirectory when renaming file in subfolder", () => {
|
||||
verifyRenamingFileInSubFolder(Tsc_WatchDirectory.NonRecursiveWatchDirectory);
|
||||
});
|
||||
|
||||
it("uses non recursive dynamic polling when renaming file in subfolder", () => {
|
||||
verifyRenamingFileInSubFolder(Tsc_WatchDirectory.DynamicPolling);
|
||||
});
|
||||
|
||||
it("when there are symlinks to folders in recursive folders", () => {
|
||||
const cwd = "/home/user/projects/myproject";
|
||||
const file1: File = {
|
||||
path: `${cwd}/src/file.ts`,
|
||||
content: `import * as a from "a"`
|
||||
};
|
||||
const tsconfig: File = {
|
||||
path: `${cwd}/tsconfig.json`,
|
||||
content: `{ "compilerOptions": { "extendedDiagnostics": true, "traceResolution": true }}`
|
||||
};
|
||||
const realA: File = {
|
||||
path: `${cwd}/node_modules/reala/index.d.ts`,
|
||||
content: `export {}`
|
||||
};
|
||||
const realB: File = {
|
||||
path: `${cwd}/node_modules/realb/index.d.ts`,
|
||||
content: `export {}`
|
||||
};
|
||||
const symLinkA: SymLink = {
|
||||
path: `${cwd}/node_modules/a`,
|
||||
symLink: `${cwd}/node_modules/reala`
|
||||
};
|
||||
const symLinkB: SymLink = {
|
||||
path: `${cwd}/node_modules/b`,
|
||||
symLink: `${cwd}/node_modules/realb`
|
||||
};
|
||||
const symLinkBInA: SymLink = {
|
||||
path: `${cwd}/node_modules/reala/node_modules/b`,
|
||||
symLink: `${cwd}/node_modules/b`
|
||||
};
|
||||
const symLinkAInB: SymLink = {
|
||||
path: `${cwd}/node_modules/realb/node_modules/a`,
|
||||
symLink: `${cwd}/node_modules/a`
|
||||
};
|
||||
const files = [file1, tsconfig, realA, realB, symLinkA, symLinkB, symLinkBInA, symLinkAInB];
|
||||
const environmentVariables = createMap<string>();
|
||||
environmentVariables.set("TSC_WATCHDIRECTORY", Tsc_WatchDirectory.NonRecursiveWatchDirectory);
|
||||
const host = createWatchedSystem(files, { environmentVariables, currentDirectory: cwd });
|
||||
createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
|
||||
checkWatchedDirectories(host, [cwd, `${cwd}/node_modules`, `${cwd}/node_modules/@types`, `${cwd}/node_modules/reala`, `${cwd}/node_modules/realb`,
|
||||
`${cwd}/node_modules/reala/node_modules`, `${cwd}/node_modules/realb/node_modules`, `${cwd}/src`], /*recursive*/ false);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@ namespace ts.projectSystem {
|
||||
return countWhere(recursiveWatchedDirs, dir => file.length > dir.length && startsWith(file, dir) && file[dir.length] === directorySeparator);
|
||||
}
|
||||
|
||||
describe("tsserverCachingFileSystemInformation:: tsserverProjectSystem CachingFileSystemInformation", () => {
|
||||
describe("tsserver:: CachingFileSystemInformation:: tsserverProjectSystem CachingFileSystemInformation", () => {
|
||||
enum CalledMapsWithSingleArg {
|
||||
fileExists = "fileExists",
|
||||
directoryExists = "directoryExists",
|
||||
+2
-2
@@ -6,7 +6,7 @@ namespace ts.projectSystem {
|
||||
return new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
|
||||
}
|
||||
|
||||
describe("compileOnSave:: affected list", () => {
|
||||
describe("tsserver:: compileOnSave:: affected list", () => {
|
||||
function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: File[] }[]) {
|
||||
const response = session.executeCommand(request).response as server.protocol.CompileOnSaveAffectedFileListSingleProject[];
|
||||
const actualResult = response.sort((list1, list2) => compareStringsCaseSensitive(list1.projectFileName, list2.projectFileName));
|
||||
@@ -552,7 +552,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
});
|
||||
|
||||
describe("compileOnSave:: EmitFile test", () => {
|
||||
describe("tsserver:: compileOnSave:: EmitFile test", () => {
|
||||
it("should respect line endings", () => {
|
||||
test("\n");
|
||||
test("\r\n");
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("tsserverLargeFileReferencedEvent:: LargeFileReferencedEvent with large file", () => {
|
||||
describe("tsserver:: LargeFileReferencedEvent with large file", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
|
||||
function getLargeFile(useLargeTsFile: boolean) {
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("tsserverProjectLoadingEvents:: ProjectLoadingStart and ProjectLoadingFinish events", () => {
|
||||
describe("tsserver:: events:: ProjectLoadingStart and ProjectLoadingFinish events", () => {
|
||||
const projectRoot = "/user/username/projects";
|
||||
const aTs: File = {
|
||||
path: `${projectRoot}/a/a.ts`,
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("tsserverProjectUpdatedInBackgroundEvent:: ProjectsUpdatedInBackground", () => {
|
||||
describe("tsserver:: ProjectsUpdatedInBackground", () => {
|
||||
function verifyFiles(caption: string, actual: ReadonlyArray<string>, expected: ReadonlyArray<string>) {
|
||||
assert.equal(actual.length, expected.length, `Incorrect number of ${caption}. Actual: ${actual} Expected: ${expected}`);
|
||||
const seen = createMap<true>();
|
||||
@@ -0,0 +1,300 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("tsserver:: ExternalProjects", () => {
|
||||
describe("correctly handling add/remove tsconfig - 1", () => {
|
||||
function verifyAddRemoveConfig(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const f2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const tsconfig = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([f1, f2]);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
|
||||
|
||||
// open external project
|
||||
const projectName = "/a/b/proj1";
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
|
||||
|
||||
// rename lib.ts to tsconfig.json
|
||||
host.reloadFS([f1, tsconfig]);
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, tsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
}
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [f1.path, tsconfig.path]);
|
||||
|
||||
// rename tsconfig.json back to lib.ts
|
||||
host.reloadFS([f1, f2]);
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
|
||||
}
|
||||
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
});
|
||||
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("correctly handling add/remove tsconfig - 2", () => {
|
||||
function verifyAddRemoveConfig(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const cLib = {
|
||||
path: "/a/b/c/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const cTsconfig = {
|
||||
path: "/a/b/c/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const dLib = {
|
||||
path: "/a/b/d/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const dTsconfig = {
|
||||
path: "/a/b/d/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const host = createServerHost([f1, cLib, cTsconfig, dLib, dTsconfig]);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
|
||||
|
||||
// open external project
|
||||
const projectName = "/a/b/proj1";
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
|
||||
|
||||
// add two config file as root files
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
}
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// remove one config file
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// remove second config file
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
|
||||
|
||||
// open two config files
|
||||
// add two config file as root files
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
}
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// close all projects - no projects should be opened
|
||||
projectService.closeExternalProject(projectName);
|
||||
projectService.checkNumberOfProjects({});
|
||||
}
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
});
|
||||
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
});
|
||||
});
|
||||
|
||||
it("correctly handles changes in lib section of config file", () => {
|
||||
const libES5 = {
|
||||
path: "/compiler/lib.es5.d.ts",
|
||||
content: "declare const eval: any"
|
||||
};
|
||||
const libES2015Promise = {
|
||||
path: "/compiler/lib.es2015.promise.d.ts",
|
||||
content: "declare class Promise<T> {}"
|
||||
};
|
||||
const app = {
|
||||
path: "/src/app.ts",
|
||||
content: "var x: Promise<string>;"
|
||||
};
|
||||
const config1 = {
|
||||
path: "/src/tsconfig.json",
|
||||
content: JSON.stringify(
|
||||
{
|
||||
compilerOptions: {
|
||||
module: "commonjs",
|
||||
target: "es5",
|
||||
noImplicitAny: true,
|
||||
sourceMap: false,
|
||||
lib: [
|
||||
"es5"
|
||||
]
|
||||
}
|
||||
})
|
||||
};
|
||||
const config2 = {
|
||||
path: config1.path,
|
||||
content: JSON.stringify(
|
||||
{
|
||||
compilerOptions: {
|
||||
module: "commonjs",
|
||||
target: "es5",
|
||||
noImplicitAny: true,
|
||||
sourceMap: false,
|
||||
lib: [
|
||||
"es5",
|
||||
"es2015.promise"
|
||||
]
|
||||
}
|
||||
})
|
||||
};
|
||||
const host = createServerHost([libES5, libES2015Promise, app, config1], { executingFilePath: "/compiler/tsc.js" });
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(app.path);
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [libES5.path, app.path, config1.path]);
|
||||
|
||||
host.reloadFS([libES5, libES2015Promise, app, config2]);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [libES5.path, libES2015Promise.path, app.path, config2.path]);
|
||||
});
|
||||
|
||||
it("should handle non-existing directories in config file", () => {
|
||||
const f = {
|
||||
path: "/a/src/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const config = {
|
||||
path: "/a/tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {},
|
||||
include: [
|
||||
"src/**/*",
|
||||
"notexistingfolder/*"
|
||||
]
|
||||
})
|
||||
};
|
||||
const host = createServerHost([f, config]);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(f.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const project = projectService.configuredProjects.get(config.path)!;
|
||||
assert.isTrue(project.hasOpenRef()); // f
|
||||
|
||||
projectService.closeClientFile(f.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
assert.strictEqual(projectService.configuredProjects.get(config.path), project);
|
||||
assert.isFalse(project.hasOpenRef()); // No files
|
||||
assert.isFalse(project.isClosed());
|
||||
|
||||
projectService.openClientFile(f.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
assert.strictEqual(projectService.configuredProjects.get(config.path), project);
|
||||
assert.isTrue(project.hasOpenRef()); // f
|
||||
assert.isFalse(project.isClosed());
|
||||
});
|
||||
|
||||
it("handles loads existing configured projects of external projects when lazyConfiguredProjectsFromExternalProject is disabled", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const config = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({})
|
||||
};
|
||||
const projectFileName = "/a/b/project.csproj";
|
||||
const host = createServerHost([f1, config]);
|
||||
const service = createProjectService(host);
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: true } });
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([f1.path, config.path]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const project = service.configuredProjects.get(config.path)!;
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
|
||||
checkProjectActualFiles(project, emptyArray);
|
||||
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: false } });
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
|
||||
checkProjectActualFiles(project, [config.path, f1.path]);
|
||||
|
||||
service.closeExternalProject(projectFileName);
|
||||
service.checkNumberOfProjects({});
|
||||
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([f1.path, config.path]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const project2 = service.configuredProjects.get(config.path)!;
|
||||
assert.equal(project2.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
|
||||
checkProjectActualFiles(project2, [config.path, f1.path]);
|
||||
});
|
||||
});
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,151 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("tsserver:: reload", () => {
|
||||
it("should work with temp file", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const tmp = {
|
||||
path: "/a/b/app.tmp",
|
||||
content: "const y = 42"
|
||||
};
|
||||
const host = createServerHost([f1, tmp]);
|
||||
const session = createSession(host);
|
||||
|
||||
// send open request
|
||||
session.executeCommand(<server.protocol.OpenRequest>{
|
||||
type: "request",
|
||||
command: "open",
|
||||
seq: 1,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
|
||||
// reload from tmp file
|
||||
session.executeCommand(<server.protocol.ReloadRequest>{
|
||||
type: "request",
|
||||
command: "reload",
|
||||
seq: 2,
|
||||
arguments: { file: f1.path, tmpfile: tmp.path }
|
||||
});
|
||||
|
||||
// verify content
|
||||
const projectServiice = session.getProjectService();
|
||||
const snap1 = projectServiice.getScriptInfo(f1.path)!.getSnapshot();
|
||||
assert.equal(getSnapshotText(snap1), tmp.content, "content should be equal to the content of temp file");
|
||||
|
||||
// reload from original file file
|
||||
session.executeCommand(<server.protocol.ReloadRequest>{
|
||||
type: "request",
|
||||
command: "reload",
|
||||
seq: 2,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
|
||||
// verify content
|
||||
const snap2 = projectServiice.getScriptInfo(f1.path)!.getSnapshot();
|
||||
assert.equal(getSnapshotText(snap2), f1.content, "content should be equal to the content of original file");
|
||||
|
||||
});
|
||||
|
||||
it("should work when script info doesnt have any project open", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const tmp = {
|
||||
path: "/a/b/app.tmp",
|
||||
content: "const y = 42"
|
||||
};
|
||||
const host = createServerHost([f1, tmp, libFile]);
|
||||
const session = createSession(host);
|
||||
const openContent = "let z = 1";
|
||||
// send open request
|
||||
session.executeCommandSeq(<server.protocol.OpenRequest>{
|
||||
command: server.protocol.CommandTypes.Open,
|
||||
arguments: { file: f1.path, fileContent: openContent }
|
||||
});
|
||||
|
||||
const projectService = session.getProjectService();
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
const info = projectService.getScriptInfo(f1.path)!;
|
||||
assert.isDefined(info);
|
||||
checkScriptInfoContents(openContent, "contents set during open request");
|
||||
|
||||
// send close request
|
||||
session.executeCommandSeq(<server.protocol.CloseRequest>{
|
||||
command: server.protocol.CommandTypes.Close,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// Can reload contents of the file when its not open and has no project
|
||||
// reload from temp file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path, tmpfile: tmp.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(tmp.content, "contents of temp file");
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// reload from own file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// Open file again without setting its content
|
||||
session.executeCommandSeq(<server.protocol.OpenRequest>{
|
||||
command: server.protocol.CommandTypes.Open,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(f1.content, "contents of file when opened without specifying contents");
|
||||
const snap = info.getSnapshot();
|
||||
|
||||
// send close request
|
||||
session.executeCommandSeq(<server.protocol.CloseRequest>{
|
||||
command: server.protocol.CommandTypes.Close,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
assert.strictEqual(info.getSnapshot(), snap);
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// reload from temp file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path, tmpfile: tmp.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(tmp.content, "contents of temp file");
|
||||
assert.notStrictEqual(info.getSnapshot(), snap);
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// reload from own file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
assert.notStrictEqual(info.getSnapshot(), snap);
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
function checkInferredProjectIsOrphan() {
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
assert.equal(info.containingProjects.length, 0);
|
||||
}
|
||||
|
||||
function checkScriptInfoAndProjects(contentsOfInfo: string, captionForContents: string) {
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
assert.strictEqual(projectService.getScriptInfo(f1.path), info);
|
||||
checkScriptInfoContents(contentsOfInfo, captionForContents);
|
||||
}
|
||||
|
||||
function checkScriptInfoContents(contentsOfInfo: string, captionForContents: string) {
|
||||
const snap = info.getSnapshot();
|
||||
assert.equal(getSnapshotText(snap), contentsOfInfo, "content should be equal to " + captionForContents);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
-449
@@ -1,452 +1,3 @@
|
||||
namespace ts.tscWatch {
|
||||
describe("resolutionCache:: tsc-watch module resolution caching", () => {
|
||||
it("works", () => {
|
||||
const root = {
|
||||
path: "/a/d/f0.ts",
|
||||
content: `import {x} from "f1"`
|
||||
};
|
||||
const imported = {
|
||||
path: "/a/f1.ts",
|
||||
content: `foo()`
|
||||
};
|
||||
|
||||
const files = [root, imported, libFile];
|
||||
const host = createWatchedSystem(files);
|
||||
const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD });
|
||||
|
||||
const f1IsNotModule = getDiagnosticOfFileFromProgram(watch(), root.path, root.content.indexOf('"f1"'), '"f1"'.length, Diagnostics.File_0_is_not_a_module, imported.path);
|
||||
const cannotFindFoo = getDiagnosticOfFileFromProgram(watch(), imported.path, imported.content.indexOf("foo"), "foo".length, Diagnostics.Cannot_find_name_0, "foo");
|
||||
|
||||
// ensure that imported file was found
|
||||
checkOutputErrorsInitial(host, [f1IsNotModule, cannotFindFoo]);
|
||||
|
||||
const originalFileExists = host.fileExists;
|
||||
{
|
||||
const newContent = `import {x} from "f1"
|
||||
var x: string = 1;`;
|
||||
root.content = newContent;
|
||||
host.reloadFS(files);
|
||||
|
||||
// patch fileExists to make sure that disk is not touched
|
||||
host.fileExists = notImplemented;
|
||||
|
||||
// trigger synchronization to make sure that import will be fetched from the cache
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
// ensure file has correct number of errors after edit
|
||||
checkOutputErrorsIncremental(host, [
|
||||
f1IsNotModule,
|
||||
getDiagnosticOfFileFromProgram(watch(), root.path, newContent.indexOf("var x") + "var ".length, "x".length, Diagnostics.Type_0_is_not_assignable_to_type_1, 1, "string"),
|
||||
cannotFindFoo
|
||||
]);
|
||||
}
|
||||
{
|
||||
let fileExistsIsCalled = false;
|
||||
host.fileExists = (fileName): boolean => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
fileExistsIsCalled = true;
|
||||
assert.isTrue(fileName.indexOf("/f2.") !== -1);
|
||||
return originalFileExists.call(host, fileName);
|
||||
};
|
||||
|
||||
root.content = `import {x} from "f2"`;
|
||||
host.reloadFS(files);
|
||||
|
||||
// trigger synchronization to make sure that LSHost will try to find 'f2' module on disk
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
// ensure file has correct number of errors after edit
|
||||
checkOutputErrorsIncremental(host, [
|
||||
getDiagnosticModuleNotFoundOfFile(watch(), root, "f2")
|
||||
]);
|
||||
|
||||
assert.isTrue(fileExistsIsCalled);
|
||||
}
|
||||
{
|
||||
let fileExistsCalled = false;
|
||||
host.fileExists = (fileName): boolean => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
fileExistsCalled = true;
|
||||
assert.isTrue(fileName.indexOf("/f1.") !== -1);
|
||||
return originalFileExists.call(host, fileName);
|
||||
};
|
||||
|
||||
const newContent = `import {x} from "f1"`;
|
||||
root.content = newContent;
|
||||
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
checkOutputErrorsIncremental(host, [f1IsNotModule, cannotFindFoo]);
|
||||
assert.isTrue(fileExistsCalled);
|
||||
}
|
||||
});
|
||||
|
||||
it("loads missing files from disk", () => {
|
||||
const root = {
|
||||
path: `/a/foo.ts`,
|
||||
content: `import {x} from "bar"`
|
||||
};
|
||||
|
||||
const imported = {
|
||||
path: `/a/bar.d.ts`,
|
||||
content: `export const y = 1;`
|
||||
};
|
||||
|
||||
const files = [root, libFile];
|
||||
const host = createWatchedSystem(files);
|
||||
const originalFileExists = host.fileExists;
|
||||
|
||||
let fileExistsCalledForBar = false;
|
||||
host.fileExists = fileName => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
if (!fileExistsCalledForBar) {
|
||||
fileExistsCalledForBar = fileName.indexOf("/bar.") !== -1;
|
||||
}
|
||||
|
||||
return originalFileExists.call(host, fileName);
|
||||
};
|
||||
|
||||
const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD });
|
||||
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
|
||||
checkOutputErrorsInitial(host, [
|
||||
getDiagnosticModuleNotFoundOfFile(watch(), root, "bar")
|
||||
]);
|
||||
|
||||
fileExistsCalledForBar = false;
|
||||
root.content = `import {y} from "bar"`;
|
||||
host.reloadFS(files.concat(imported));
|
||||
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
|
||||
});
|
||||
|
||||
it("should compile correctly when resolved module goes missing and then comes back (module is not part of the root)", () => {
|
||||
const root = {
|
||||
path: `/a/foo.ts`,
|
||||
content: `import {x} from "bar"`
|
||||
};
|
||||
|
||||
const imported = {
|
||||
path: `/a/bar.d.ts`,
|
||||
content: `export const y = 1;export const x = 10;`
|
||||
};
|
||||
|
||||
const files = [root, libFile];
|
||||
const filesWithImported = files.concat(imported);
|
||||
const host = createWatchedSystem(filesWithImported);
|
||||
const originalFileExists = host.fileExists;
|
||||
let fileExistsCalledForBar = false;
|
||||
host.fileExists = fileName => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
if (!fileExistsCalledForBar) {
|
||||
fileExistsCalledForBar = fileName.indexOf("/bar.") !== -1;
|
||||
}
|
||||
return originalFileExists.call(host, fileName);
|
||||
};
|
||||
|
||||
const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD });
|
||||
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
|
||||
fileExistsCalledForBar = false;
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
|
||||
checkOutputErrorsIncremental(host, [
|
||||
getDiagnosticModuleNotFoundOfFile(watch(), root, "bar")
|
||||
]);
|
||||
|
||||
fileExistsCalledForBar = false;
|
||||
host.reloadFS(filesWithImported);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
|
||||
});
|
||||
|
||||
it("works when module resolution changes to ambient module", () => {
|
||||
const root = {
|
||||
path: "/a/b/foo.ts",
|
||||
content: `import * as fs from "fs";`
|
||||
};
|
||||
|
||||
const packageJson = {
|
||||
path: "/a/b/node_modules/@types/node/package.json",
|
||||
content: `
|
||||
{
|
||||
"main": ""
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
const nodeType = {
|
||||
path: "/a/b/node_modules/@types/node/index.d.ts",
|
||||
content: `
|
||||
declare module "fs" {
|
||||
export interface Stats {
|
||||
isFile(): boolean;
|
||||
}
|
||||
}`
|
||||
};
|
||||
|
||||
const files = [root, libFile];
|
||||
const filesWithNodeType = files.concat(packageJson, nodeType);
|
||||
const host = createWatchedSystem(files, { currentDirectory: "/a/b" });
|
||||
|
||||
const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { });
|
||||
|
||||
checkOutputErrorsInitial(host, [
|
||||
getDiagnosticModuleNotFoundOfFile(watch(), root, "fs")
|
||||
]);
|
||||
|
||||
host.reloadFS(filesWithNodeType);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
});
|
||||
|
||||
it("works when included file with ambient module changes", () => {
|
||||
const root = {
|
||||
path: "/a/b/foo.ts",
|
||||
content: `
|
||||
import * as fs from "fs";
|
||||
import * as u from "url";
|
||||
`
|
||||
};
|
||||
|
||||
const file = {
|
||||
path: "/a/b/bar.d.ts",
|
||||
content: `
|
||||
declare module "url" {
|
||||
export interface Url {
|
||||
href?: string;
|
||||
}
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
const fileContentWithFS = `
|
||||
declare module "fs" {
|
||||
export interface Stats {
|
||||
isFile(): boolean;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const files = [root, file, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory: "/a/b" });
|
||||
|
||||
const watch = createWatchOfFilesAndCompilerOptions([root.path, file.path], host, {});
|
||||
|
||||
checkOutputErrorsInitial(host, [
|
||||
getDiagnosticModuleNotFoundOfFile(watch(), root, "fs")
|
||||
]);
|
||||
|
||||
file.content += fileContentWithFS;
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
});
|
||||
|
||||
it("works when reusing program with files from external library", () => {
|
||||
interface ExpectedFile { path: string; isExpectedToEmit?: boolean; content?: string; }
|
||||
const configDir = "/a/b/projects/myProject/src/";
|
||||
const file1: File = {
|
||||
path: configDir + "file1.ts",
|
||||
content: 'import module1 = require("module1");\nmodule1("hello");'
|
||||
};
|
||||
const file2: File = {
|
||||
path: configDir + "file2.ts",
|
||||
content: 'import module11 = require("module1");\nmodule11("hello");'
|
||||
};
|
||||
const module1: File = {
|
||||
path: "/a/b/projects/myProject/node_modules/module1/index.js",
|
||||
content: "module.exports = options => { return options.toString(); }"
|
||||
};
|
||||
const configFile: File = {
|
||||
path: configDir + "tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
allowJs: true,
|
||||
rootDir: ".",
|
||||
outDir: "../dist",
|
||||
moduleResolution: "node",
|
||||
maxNodeModuleJsDepth: 1
|
||||
}
|
||||
})
|
||||
};
|
||||
const outDirFolder = "/a/b/projects/myProject/dist/";
|
||||
const programFiles = [file1, file2, module1, libFile];
|
||||
const host = createWatchedSystem(programFiles.concat(configFile), { currentDirectory: "/a/b/projects/myProject/" });
|
||||
const watch = createWatchOfConfigFile(configFile.path, host);
|
||||
checkProgramActualFiles(watch(), programFiles.map(f => f.path));
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
const expectedFiles: ExpectedFile[] = [
|
||||
createExpectedEmittedFile(file1),
|
||||
createExpectedEmittedFile(file2),
|
||||
createExpectedToNotEmitFile("index.js"),
|
||||
createExpectedToNotEmitFile("src/index.js"),
|
||||
createExpectedToNotEmitFile("src/file1.js"),
|
||||
createExpectedToNotEmitFile("src/file2.js"),
|
||||
createExpectedToNotEmitFile("lib.js"),
|
||||
createExpectedToNotEmitFile("lib.d.ts")
|
||||
];
|
||||
verifyExpectedFiles(expectedFiles);
|
||||
|
||||
file1.content += "\n;";
|
||||
expectedFiles[0].content += ";\n"; // Only emit file1 with this change
|
||||
expectedFiles[1].isExpectedToEmit = false;
|
||||
host.reloadFS(programFiles.concat(configFile));
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkProgramActualFiles(watch(), programFiles.map(f => f.path));
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
verifyExpectedFiles(expectedFiles);
|
||||
|
||||
|
||||
function verifyExpectedFiles(expectedFiles: ExpectedFile[]) {
|
||||
forEach(expectedFiles, f => {
|
||||
assert.equal(!!host.fileExists(f.path), f.isExpectedToEmit, "File " + f.path + " is expected to " + (f.isExpectedToEmit ? "emit" : "not emit"));
|
||||
if (f.isExpectedToEmit) {
|
||||
assert.equal(host.readFile(f.path), f.content, "Expected contents of " + f.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createExpectedToNotEmitFile(fileName: string): ExpectedFile {
|
||||
return {
|
||||
path: outDirFolder + fileName,
|
||||
isExpectedToEmit: false
|
||||
};
|
||||
}
|
||||
|
||||
function createExpectedEmittedFile(file: File): ExpectedFile {
|
||||
return {
|
||||
path: removeFileExtension(file.path.replace(configDir, outDirFolder)) + Extension.Js,
|
||||
isExpectedToEmit: true,
|
||||
content: '"use strict";\nexports.__esModule = true;\n' + file.content.replace("import", "var") + "\n"
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
it("works when renaming node_modules folder that already contains @types folder", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const file: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
content: `import * as q from "qqq";`
|
||||
};
|
||||
const module: File = {
|
||||
path: `${currentDirectory}/node_modules2/@types/qqq/index.d.ts`,
|
||||
content: "export {}"
|
||||
};
|
||||
const files = [file, module, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const watch = createWatchOfFilesAndCompilerOptions([file.path], host);
|
||||
|
||||
checkProgramActualFiles(watch(), [file.path, libFile.path]);
|
||||
checkOutputErrorsInitial(host, [getDiagnosticModuleNotFoundOfFile(watch(), file, "qqq")]);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [`${currentDirectory}/node_modules`, `${currentDirectory}/node_modules/@types`], /*recursive*/ true);
|
||||
|
||||
host.renameFolder(`${currentDirectory}/node_modules2`, `${currentDirectory}/node_modules`);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkProgramActualFiles(watch(), [file.path, libFile.path, `${currentDirectory}/node_modules/@types/qqq/index.d.ts`]);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
});
|
||||
|
||||
describe("ignores files/folder changes in node_modules that start with '.'", () => {
|
||||
const projectPath = "/user/username/projects/project";
|
||||
const npmCacheFile: File = {
|
||||
path: `${projectPath}/node_modules/.cache/babel-loader/89c02171edab901b9926470ba6d5677e.ts`,
|
||||
content: JSON.stringify({ something: 10 })
|
||||
};
|
||||
const file1: File = {
|
||||
path: `${projectPath}/test.ts`,
|
||||
content: `import { x } from "somemodule";`
|
||||
};
|
||||
const file2: File = {
|
||||
path: `${projectPath}/node_modules/somemodule/index.d.ts`,
|
||||
content: `export const x = 10;`
|
||||
};
|
||||
const files = [libFile, file1, file2];
|
||||
const expectedFiles = files.map(f => f.path);
|
||||
it("when watching node_modules in inferred project for failed lookup", () => {
|
||||
const host = createWatchedSystem(files);
|
||||
const watch = createWatchOfFilesAndCompilerOptions([file1.path], host, {}, /*maxNumberOfFilesToIterateForInvalidation*/ 1);
|
||||
checkProgramActualFiles(watch(), expectedFiles);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
|
||||
host.ensureFileOrFolder(npmCacheFile);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
});
|
||||
it("when watching node_modules as part of wild card directories in config project", () => {
|
||||
const config: File = {
|
||||
path: `${projectPath}/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const host = createWatchedSystem(files.concat(config));
|
||||
const watch = createWatchOfConfigFile(config.path, host);
|
||||
checkProgramActualFiles(watch(), expectedFiles);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
|
||||
host.ensureFileOrFolder(npmCacheFile);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolutionCache:: tsc-watch with modules linked to sibling folder", () => {
|
||||
const projectRoot = "/user/username/projects/project";
|
||||
const mainPackageRoot = `${projectRoot}/main`;
|
||||
const linkedPackageRoot = `${projectRoot}/linked-package`;
|
||||
const mainFile: File = {
|
||||
path: `${mainPackageRoot}/index.ts`,
|
||||
content: "import { Foo } from '@scoped/linked-package'"
|
||||
};
|
||||
const config: File = {
|
||||
path: `${mainPackageRoot}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: { module: "commonjs", moduleResolution: "node", baseUrl: ".", rootDir: "." },
|
||||
files: ["index.ts"]
|
||||
})
|
||||
};
|
||||
const linkedPackageInMain: SymLink = {
|
||||
path: `${mainPackageRoot}/node_modules/@scoped/linked-package`,
|
||||
symLink: `${linkedPackageRoot}`
|
||||
};
|
||||
const linkedPackageJson: File = {
|
||||
path: `${linkedPackageRoot}/package.json`,
|
||||
content: JSON.stringify({ name: "@scoped/linked-package", version: "0.0.1", types: "dist/index.d.ts", main: "dist/index.js" })
|
||||
};
|
||||
const linkedPackageIndex: File = {
|
||||
path: `${linkedPackageRoot}/dist/index.d.ts`,
|
||||
content: "export * from './other';"
|
||||
};
|
||||
const linkedPackageOther: File = {
|
||||
path: `${linkedPackageRoot}/dist/other.d.ts`,
|
||||
content: 'export declare const Foo = "BAR";'
|
||||
};
|
||||
|
||||
it("verify watched directories", () => {
|
||||
const files = [libFile, mainFile, config, linkedPackageInMain, linkedPackageJson, linkedPackageIndex, linkedPackageOther];
|
||||
const host = createWatchedSystem(files, { currentDirectory: mainPackageRoot });
|
||||
createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkWatchedFilesDetailed(host, [libFile.path, mainFile.path, config.path, linkedPackageIndex.path, linkedPackageOther.path], 1);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, [`${mainPackageRoot}/@scoped`, `${mainPackageRoot}/node_modules`, linkedPackageRoot, `${mainPackageRoot}/node_modules/@types`, `${projectRoot}/node_modules/@types`], 1, /*recursive*/ true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
namespace ts.projectSystem {
|
||||
function createHostModuleResolutionTrace(host: TestServerHost & ModuleResolutionHost) {
|
||||
const resolutionTrace: string[] = [];
|
||||
File renamed without changes.
@@ -0,0 +1,229 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("tsserver:: with skipLibCheck", () => {
|
||||
it("should be turned on for js-only inferred projects", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/file1.js",
|
||||
content: `
|
||||
/// <reference path="file2.d.ts" />
|
||||
var x = 1;`
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/file2.d.ts",
|
||||
content: `
|
||||
interface T {
|
||||
name: string;
|
||||
};
|
||||
interface T {
|
||||
name: number;
|
||||
};`
|
||||
};
|
||||
const host = createServerHost([file1, file2]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([file1, file2], session);
|
||||
|
||||
const file2GetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: file2.path }
|
||||
);
|
||||
let errorResult = <protocol.Diagnostic[]>session.executeCommand(file2GetErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 0);
|
||||
|
||||
const closeFileRequest = makeSessionRequest<protocol.FileRequestArgs>(CommandNames.Close, { file: file1.path });
|
||||
session.executeCommand(closeFileRequest);
|
||||
errorResult = <protocol.Diagnostic[]>session.executeCommand(file2GetErrRequest).response;
|
||||
assert.isTrue(errorResult.length !== 0);
|
||||
|
||||
openFilesForSession([file1], session);
|
||||
errorResult = <protocol.Diagnostic[]>session.executeCommand(file2GetErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 0);
|
||||
});
|
||||
|
||||
it("should be turned on for js-only external projects", () => {
|
||||
const jsFile = {
|
||||
path: "/a/b/file1.js",
|
||||
content: "let x =1;"
|
||||
};
|
||||
const dTsFile = {
|
||||
path: "/a/b/file2.d.ts",
|
||||
content: `
|
||||
interface T {
|
||||
name: string;
|
||||
};
|
||||
interface T {
|
||||
name: number;
|
||||
};`
|
||||
};
|
||||
const host = createServerHost([jsFile, dTsFile]);
|
||||
const session = createSession(host);
|
||||
|
||||
const openExternalProjectRequest = makeSessionRequest<protocol.OpenExternalProjectArgs>(
|
||||
CommandNames.OpenExternalProject,
|
||||
{
|
||||
projectFileName: "project1",
|
||||
rootFiles: toExternalFiles([jsFile.path, dTsFile.path]),
|
||||
options: {}
|
||||
}
|
||||
);
|
||||
session.executeCommand(openExternalProjectRequest);
|
||||
|
||||
const dTsFileGetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: dTsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(dTsFileGetErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 0);
|
||||
});
|
||||
|
||||
it("should be turned on for js-only external projects with skipLibCheck=false", () => {
|
||||
const jsFile = {
|
||||
path: "/a/b/file1.js",
|
||||
content: "let x =1;"
|
||||
};
|
||||
const dTsFile = {
|
||||
path: "/a/b/file2.d.ts",
|
||||
content: `
|
||||
interface T {
|
||||
name: string;
|
||||
};
|
||||
interface T {
|
||||
name: number;
|
||||
};`
|
||||
};
|
||||
const host = createServerHost([jsFile, dTsFile]);
|
||||
const session = createSession(host);
|
||||
|
||||
const openExternalProjectRequest = makeSessionRequest<protocol.OpenExternalProjectArgs>(
|
||||
CommandNames.OpenExternalProject,
|
||||
{
|
||||
projectFileName: "project1",
|
||||
rootFiles: toExternalFiles([jsFile.path, dTsFile.path]),
|
||||
options: { skipLibCheck: false }
|
||||
}
|
||||
);
|
||||
session.executeCommand(openExternalProjectRequest);
|
||||
|
||||
const dTsFileGetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: dTsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(dTsFileGetErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 0);
|
||||
});
|
||||
|
||||
it("should not report bind errors for declaration files with skipLibCheck=true", () => {
|
||||
const jsconfigFile = {
|
||||
path: "/a/jsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const dTsFile1 = {
|
||||
path: "/a/dTsFile1.d.ts",
|
||||
content: `
|
||||
declare var x: number;`
|
||||
};
|
||||
const dTsFile2 = {
|
||||
path: "/a/dTsFile2.d.ts",
|
||||
content: `
|
||||
declare var x: string;`
|
||||
};
|
||||
const host = createServerHost([jsconfigFile, jsFile, dTsFile1, dTsFile2]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const dTsFile1GetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: dTsFile1.path }
|
||||
);
|
||||
const error1Result = <protocol.Diagnostic[]>session.executeCommand(dTsFile1GetErrRequest).response;
|
||||
assert.isTrue(error1Result.length === 0);
|
||||
|
||||
const dTsFile2GetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: dTsFile2.path }
|
||||
);
|
||||
const error2Result = <protocol.Diagnostic[]>session.executeCommand(dTsFile2GetErrRequest).response;
|
||||
assert.isTrue(error2Result.length === 0);
|
||||
});
|
||||
|
||||
it("should report semantic errors for loose JS files with '// @ts-check' and skipLibCheck=true", () => {
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: `
|
||||
// @ts-check
|
||||
let x = 1;
|
||||
x === "string";`
|
||||
};
|
||||
|
||||
const host = createServerHost([jsFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: jsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 1);
|
||||
assert.equal(errorResult[0].code, Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap.code);
|
||||
});
|
||||
|
||||
it("should report semantic errors for configured js project with '// @ts-check' and skipLibCheck=true", () => {
|
||||
const jsconfigFile = {
|
||||
path: "/a/jsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: `
|
||||
// @ts-check
|
||||
let x = 1;
|
||||
x === "string";`
|
||||
};
|
||||
|
||||
const host = createServerHost([jsconfigFile, jsFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: jsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 1);
|
||||
assert.equal(errorResult[0].code, Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap.code);
|
||||
});
|
||||
|
||||
it("should report semantic errors for configured js project with checkJs=true and skipLibCheck=true", () => {
|
||||
const jsconfigFile = {
|
||||
path: "/a/jsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
checkJs: true,
|
||||
skipLibCheck: true
|
||||
},
|
||||
})
|
||||
};
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: `let x = 1;
|
||||
x === "string";`
|
||||
};
|
||||
|
||||
const host = createServerHost([jsconfigFile, jsFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: jsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 1);
|
||||
assert.equal(errorResult[0].code, Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap.code);
|
||||
});
|
||||
});
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("tsserverProjectSystem with symLinks", () => {
|
||||
describe("tsserver:: symLinks", () => {
|
||||
it("rename in common file renames all project", () => {
|
||||
const projects = "/users/username/projects";
|
||||
const folderA = `${projects}/a`;
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,135 @@
|
||||
namespace ts.projectSystem {
|
||||
import Tsc_WatchDirectory = TestFSWithWatch.Tsc_WatchDirectory;
|
||||
describe("watchEnvironment:: tsserverProjectSystem watchDirectories implementation", () => {
|
||||
function verifyCompletionListWithNewFileInSubFolder(tscWatchDirectory: Tsc_WatchDirectory) {
|
||||
const projectFolder = "/a/username/project";
|
||||
const projectSrcFolder = `${projectFolder}/src`;
|
||||
const configFile: File = {
|
||||
path: `${projectFolder}/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const index: File = {
|
||||
path: `${projectSrcFolder}/index.ts`,
|
||||
content: `import {} from "./"`
|
||||
};
|
||||
const file1: File = {
|
||||
path: `${projectSrcFolder}/file1.ts`,
|
||||
content: ""
|
||||
};
|
||||
|
||||
const files = [index, file1, configFile, libFile];
|
||||
const fileNames = files.map(file => file.path);
|
||||
// All closed files(files other than index), project folder, project/src folder and project/node_modules/@types folder
|
||||
const expectedWatchedFiles = arrayToMap(fileNames.slice(1), s => s, () => 1);
|
||||
const expectedWatchedDirectories = createMap<number>();
|
||||
const mapOfDirectories = tscWatchDirectory === Tsc_WatchDirectory.NonRecursiveWatchDirectory ?
|
||||
expectedWatchedDirectories :
|
||||
tscWatchDirectory === Tsc_WatchDirectory.WatchFile ?
|
||||
expectedWatchedFiles :
|
||||
createMap();
|
||||
// For failed resolution lookup and tsconfig files => cached so only watched only once
|
||||
mapOfDirectories.set(projectFolder, 1);
|
||||
// Through above recursive watches
|
||||
mapOfDirectories.set(projectSrcFolder, 1);
|
||||
// node_modules/@types folder
|
||||
mapOfDirectories.set(`${projectFolder}/${nodeModulesAtTypes}`, 1);
|
||||
const expectedCompletions = ["file1"];
|
||||
const completionPosition = index.content.lastIndexOf('"');
|
||||
const environmentVariables = createMap<string>();
|
||||
environmentVariables.set("TSC_WATCHDIRECTORY", tscWatchDirectory);
|
||||
const host = createServerHost(files, { environmentVariables });
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(index.path);
|
||||
|
||||
const project = Debug.assertDefined(projectService.configuredProjects.get(configFile.path));
|
||||
verifyProjectAndCompletions();
|
||||
|
||||
// Add file2
|
||||
const file2: File = {
|
||||
path: `${projectSrcFolder}/file2.ts`,
|
||||
content: ""
|
||||
};
|
||||
files.push(file2);
|
||||
fileNames.push(file2.path);
|
||||
expectedWatchedFiles.set(file2.path, 1);
|
||||
expectedCompletions.push("file2");
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
assert.equal(projectService.configuredProjects.get(configFile.path), project);
|
||||
verifyProjectAndCompletions();
|
||||
|
||||
function verifyProjectAndCompletions() {
|
||||
const completions = project.getLanguageService().getCompletionsAtPosition(index.path, completionPosition, { includeExternalModuleExports: false, includeInsertTextCompletions: false })!;
|
||||
checkArray("Completion Entries", completions.entries.map(e => e.name), expectedCompletions);
|
||||
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
|
||||
|
||||
checkWatchedFilesDetailed(host, expectedWatchedFiles);
|
||||
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, /*recursive*/ false);
|
||||
checkProjectActualFiles(project, fileNames);
|
||||
}
|
||||
}
|
||||
|
||||
it("uses watchFile when file is added to subfolder, completion list has new file", () => {
|
||||
verifyCompletionListWithNewFileInSubFolder(Tsc_WatchDirectory.WatchFile);
|
||||
});
|
||||
|
||||
it("uses non recursive watchDirectory when file is added to subfolder, completion list has new file", () => {
|
||||
verifyCompletionListWithNewFileInSubFolder(Tsc_WatchDirectory.NonRecursiveWatchDirectory);
|
||||
});
|
||||
|
||||
it("uses dynamic polling when file is added to subfolder, completion list has new file", () => {
|
||||
verifyCompletionListWithNewFileInSubFolder(Tsc_WatchDirectory.DynamicPolling);
|
||||
});
|
||||
});
|
||||
|
||||
describe("watchEnvironment:: tsserverProjectSystem Watched recursive directories with windows style file system", () => {
|
||||
function verifyWatchedDirectories(rootedPath: string, useProjectAtRoot: boolean) {
|
||||
const root = useProjectAtRoot ? rootedPath : `${rootedPath}myfolder/allproject/`;
|
||||
const configFile: File = {
|
||||
path: root + "project/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const file1: File = {
|
||||
path: root + "project/file1.ts",
|
||||
content: "let x = 10;"
|
||||
};
|
||||
const file2: File = {
|
||||
path: root + "project/file2.ts",
|
||||
content: "let y = 10;"
|
||||
};
|
||||
const files = [configFile, file1, file2, libFile];
|
||||
const host = createServerHost(files, { useWindowsStylePaths: true });
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(file1.path);
|
||||
const project = projectService.configuredProjects.get(configFile.path)!;
|
||||
assert.isDefined(project);
|
||||
const winsowsStyleLibFilePath = "c:/" + libFile.path.substring(1);
|
||||
checkProjectActualFiles(project, files.map(f => f === libFile ? winsowsStyleLibFilePath : f.path));
|
||||
checkWatchedFiles(host, mapDefined(files, f => f === libFile ? winsowsStyleLibFilePath : f === file1 ? undefined : f.path));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [
|
||||
root + "project",
|
||||
root + "project/node_modules/@types"
|
||||
].concat(useProjectAtRoot ? [] : [root + nodeModulesAtTypes]), /*recursive*/ true);
|
||||
}
|
||||
|
||||
function verifyRootedDirectoryWatch(rootedPath: string) {
|
||||
it("When project is in rootFolder of style c:/", () => {
|
||||
verifyWatchedDirectories(rootedPath, /*useProjectAtRoot*/ true);
|
||||
});
|
||||
|
||||
it("When files at some folder other than root", () => {
|
||||
verifyWatchedDirectories(rootedPath, /*useProjectAtRoot*/ false);
|
||||
});
|
||||
}
|
||||
|
||||
describe("for rootFolder of style c:/", () => {
|
||||
verifyRootedDirectoryWatch("c:/");
|
||||
});
|
||||
|
||||
describe("for rootFolder of style c:/users/username", () => {
|
||||
verifyRootedDirectoryWatch("c:/users/username/");
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -3280,305 +3280,6 @@ var x = 10;`
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem external projects", () => {
|
||||
describe("correctly handling add/remove tsconfig - 1", () => {
|
||||
function verifyAddRemoveConfig(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const f2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const tsconfig = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([f1, f2]);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
|
||||
|
||||
// open external project
|
||||
const projectName = "/a/b/proj1";
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
|
||||
|
||||
// rename lib.ts to tsconfig.json
|
||||
host.reloadFS([f1, tsconfig]);
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, tsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
}
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [f1.path, tsconfig.path]);
|
||||
|
||||
// rename tsconfig.json back to lib.ts
|
||||
host.reloadFS([f1, f2]);
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, f2.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
|
||||
}
|
||||
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
});
|
||||
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("correctly handling add/remove tsconfig - 2", () => {
|
||||
function verifyAddRemoveConfig(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const cLib = {
|
||||
path: "/a/b/c/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const cTsconfig = {
|
||||
path: "/a/b/c/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const dLib = {
|
||||
path: "/a/b/d/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const dTsconfig = {
|
||||
path: "/a/b/d/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const host = createServerHost([f1, cLib, cTsconfig, dLib, dTsconfig]);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
|
||||
|
||||
// open external project
|
||||
const projectName = "/a/b/proj1";
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
|
||||
|
||||
// add two config file as root files
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
}
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// remove one config file
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// remove second config file
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path]),
|
||||
options: {}
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
|
||||
|
||||
// open two config files
|
||||
// add two config file as root files
|
||||
projectService.openExternalProject({
|
||||
projectFileName: projectName,
|
||||
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
|
||||
options: {}
|
||||
});
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 2 });
|
||||
if (lazyConfiguredProjectsFromExternalProject) {
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
|
||||
projectService.ensureInferredProjectsUpToDate_TestOnly();
|
||||
}
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
|
||||
|
||||
// close all projects - no projects should be opened
|
||||
projectService.closeExternalProject(projectName);
|
||||
projectService.checkNumberOfProjects({});
|
||||
}
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
});
|
||||
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
|
||||
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
});
|
||||
});
|
||||
|
||||
it("correctly handles changes in lib section of config file", () => {
|
||||
const libES5 = {
|
||||
path: "/compiler/lib.es5.d.ts",
|
||||
content: "declare const eval: any"
|
||||
};
|
||||
const libES2015Promise = {
|
||||
path: "/compiler/lib.es2015.promise.d.ts",
|
||||
content: "declare class Promise<T> {}"
|
||||
};
|
||||
const app = {
|
||||
path: "/src/app.ts",
|
||||
content: "var x: Promise<string>;"
|
||||
};
|
||||
const config1 = {
|
||||
path: "/src/tsconfig.json",
|
||||
content: JSON.stringify(
|
||||
{
|
||||
compilerOptions: {
|
||||
module: "commonjs",
|
||||
target: "es5",
|
||||
noImplicitAny: true,
|
||||
sourceMap: false,
|
||||
lib: [
|
||||
"es5"
|
||||
]
|
||||
}
|
||||
})
|
||||
};
|
||||
const config2 = {
|
||||
path: config1.path,
|
||||
content: JSON.stringify(
|
||||
{
|
||||
compilerOptions: {
|
||||
module: "commonjs",
|
||||
target: "es5",
|
||||
noImplicitAny: true,
|
||||
sourceMap: false,
|
||||
lib: [
|
||||
"es5",
|
||||
"es2015.promise"
|
||||
]
|
||||
}
|
||||
})
|
||||
};
|
||||
const host = createServerHost([libES5, libES2015Promise, app, config1], { executingFilePath: "/compiler/tsc.js" });
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(app.path);
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [libES5.path, app.path, config1.path]);
|
||||
|
||||
host.reloadFS([libES5, libES2015Promise, app, config2]);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [libES5.path, libES2015Promise.path, app.path, config2.path]);
|
||||
});
|
||||
|
||||
it("should handle non-existing directories in config file", () => {
|
||||
const f = {
|
||||
path: "/a/src/app.ts",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const config = {
|
||||
path: "/a/tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {},
|
||||
include: [
|
||||
"src/**/*",
|
||||
"notexistingfolder/*"
|
||||
]
|
||||
})
|
||||
};
|
||||
const host = createServerHost([f, config]);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(f.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const project = projectService.configuredProjects.get(config.path)!;
|
||||
assert.isTrue(project.hasOpenRef()); // f
|
||||
|
||||
projectService.closeClientFile(f.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
assert.strictEqual(projectService.configuredProjects.get(config.path), project);
|
||||
assert.isFalse(project.hasOpenRef()); // No files
|
||||
assert.isFalse(project.isClosed());
|
||||
|
||||
projectService.openClientFile(f.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
assert.strictEqual(projectService.configuredProjects.get(config.path), project);
|
||||
assert.isTrue(project.hasOpenRef()); // f
|
||||
assert.isFalse(project.isClosed());
|
||||
});
|
||||
|
||||
it("handles loads existing configured projects of external projects when lazyConfiguredProjectsFromExternalProject is disabled", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const config = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({})
|
||||
};
|
||||
const projectFileName = "/a/b/project.csproj";
|
||||
const host = createServerHost([f1, config]);
|
||||
const service = createProjectService(host);
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: true } });
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([f1.path, config.path]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const project = service.configuredProjects.get(config.path)!;
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
|
||||
checkProjectActualFiles(project, emptyArray);
|
||||
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: false } });
|
||||
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
|
||||
checkProjectActualFiles(project, [config.path, f1.path]);
|
||||
|
||||
service.closeExternalProject(projectFileName);
|
||||
service.checkNumberOfProjects({});
|
||||
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([f1.path, config.path]),
|
||||
options: {}
|
||||
});
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const project2 = service.configuredProjects.get(config.path)!;
|
||||
assert.equal(project2.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
|
||||
checkProjectActualFiles(project2, [config.path, f1.path]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem prefer typings to js", () => {
|
||||
it("during second resolution pass", () => {
|
||||
const typingsCacheLocation = "/a/typings";
|
||||
@@ -3770,234 +3471,6 @@ var x = 10;`
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem skipLibCheck", () => {
|
||||
it("should be turned on for js-only inferred projects", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/file1.js",
|
||||
content: `
|
||||
/// <reference path="file2.d.ts" />
|
||||
var x = 1;`
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/file2.d.ts",
|
||||
content: `
|
||||
interface T {
|
||||
name: string;
|
||||
};
|
||||
interface T {
|
||||
name: number;
|
||||
};`
|
||||
};
|
||||
const host = createServerHost([file1, file2]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([file1, file2], session);
|
||||
|
||||
const file2GetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: file2.path }
|
||||
);
|
||||
let errorResult = <protocol.Diagnostic[]>session.executeCommand(file2GetErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 0);
|
||||
|
||||
const closeFileRequest = makeSessionRequest<protocol.FileRequestArgs>(CommandNames.Close, { file: file1.path });
|
||||
session.executeCommand(closeFileRequest);
|
||||
errorResult = <protocol.Diagnostic[]>session.executeCommand(file2GetErrRequest).response;
|
||||
assert.isTrue(errorResult.length !== 0);
|
||||
|
||||
openFilesForSession([file1], session);
|
||||
errorResult = <protocol.Diagnostic[]>session.executeCommand(file2GetErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 0);
|
||||
});
|
||||
|
||||
it("should be turned on for js-only external projects", () => {
|
||||
const jsFile = {
|
||||
path: "/a/b/file1.js",
|
||||
content: "let x =1;"
|
||||
};
|
||||
const dTsFile = {
|
||||
path: "/a/b/file2.d.ts",
|
||||
content: `
|
||||
interface T {
|
||||
name: string;
|
||||
};
|
||||
interface T {
|
||||
name: number;
|
||||
};`
|
||||
};
|
||||
const host = createServerHost([jsFile, dTsFile]);
|
||||
const session = createSession(host);
|
||||
|
||||
const openExternalProjectRequest = makeSessionRequest<protocol.OpenExternalProjectArgs>(
|
||||
CommandNames.OpenExternalProject,
|
||||
{
|
||||
projectFileName: "project1",
|
||||
rootFiles: toExternalFiles([jsFile.path, dTsFile.path]),
|
||||
options: {}
|
||||
}
|
||||
);
|
||||
session.executeCommand(openExternalProjectRequest);
|
||||
|
||||
const dTsFileGetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: dTsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(dTsFileGetErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 0);
|
||||
});
|
||||
|
||||
it("should be turned on for js-only external projects with skipLibCheck=false", () => {
|
||||
const jsFile = {
|
||||
path: "/a/b/file1.js",
|
||||
content: "let x =1;"
|
||||
};
|
||||
const dTsFile = {
|
||||
path: "/a/b/file2.d.ts",
|
||||
content: `
|
||||
interface T {
|
||||
name: string;
|
||||
};
|
||||
interface T {
|
||||
name: number;
|
||||
};`
|
||||
};
|
||||
const host = createServerHost([jsFile, dTsFile]);
|
||||
const session = createSession(host);
|
||||
|
||||
const openExternalProjectRequest = makeSessionRequest<protocol.OpenExternalProjectArgs>(
|
||||
CommandNames.OpenExternalProject,
|
||||
{
|
||||
projectFileName: "project1",
|
||||
rootFiles: toExternalFiles([jsFile.path, dTsFile.path]),
|
||||
options: { skipLibCheck: false }
|
||||
}
|
||||
);
|
||||
session.executeCommand(openExternalProjectRequest);
|
||||
|
||||
const dTsFileGetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: dTsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(dTsFileGetErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 0);
|
||||
});
|
||||
|
||||
it("should not report bind errors for declaration files with skipLibCheck=true", () => {
|
||||
const jsconfigFile = {
|
||||
path: "/a/jsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: "let x = 1;"
|
||||
};
|
||||
const dTsFile1 = {
|
||||
path: "/a/dTsFile1.d.ts",
|
||||
content: `
|
||||
declare var x: number;`
|
||||
};
|
||||
const dTsFile2 = {
|
||||
path: "/a/dTsFile2.d.ts",
|
||||
content: `
|
||||
declare var x: string;`
|
||||
};
|
||||
const host = createServerHost([jsconfigFile, jsFile, dTsFile1, dTsFile2]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const dTsFile1GetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: dTsFile1.path }
|
||||
);
|
||||
const error1Result = <protocol.Diagnostic[]>session.executeCommand(dTsFile1GetErrRequest).response;
|
||||
assert.isTrue(error1Result.length === 0);
|
||||
|
||||
const dTsFile2GetErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: dTsFile2.path }
|
||||
);
|
||||
const error2Result = <protocol.Diagnostic[]>session.executeCommand(dTsFile2GetErrRequest).response;
|
||||
assert.isTrue(error2Result.length === 0);
|
||||
});
|
||||
|
||||
it("should report semantic errors for loose JS files with '// @ts-check' and skipLibCheck=true", () => {
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: `
|
||||
// @ts-check
|
||||
let x = 1;
|
||||
x === "string";`
|
||||
};
|
||||
|
||||
const host = createServerHost([jsFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: jsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 1);
|
||||
assert.equal(errorResult[0].code, Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap.code);
|
||||
});
|
||||
|
||||
it("should report semantic errors for configured js project with '// @ts-check' and skipLibCheck=true", () => {
|
||||
const jsconfigFile = {
|
||||
path: "/a/jsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: `
|
||||
// @ts-check
|
||||
let x = 1;
|
||||
x === "string";`
|
||||
};
|
||||
|
||||
const host = createServerHost([jsconfigFile, jsFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: jsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 1);
|
||||
assert.equal(errorResult[0].code, Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap.code);
|
||||
});
|
||||
|
||||
it("should report semantic errors for configured js project with checkJs=true and skipLibCheck=true", () => {
|
||||
const jsconfigFile = {
|
||||
path: "/a/jsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
checkJs: true,
|
||||
skipLibCheck: true
|
||||
},
|
||||
})
|
||||
};
|
||||
const jsFile = {
|
||||
path: "/a/jsFile.js",
|
||||
content: `let x = 1;
|
||||
x === "string";`
|
||||
};
|
||||
|
||||
const host = createServerHost([jsconfigFile, jsFile]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([jsFile], session);
|
||||
|
||||
const getErrRequest = makeSessionRequest<protocol.SemanticDiagnosticsSyncRequestArgs>(
|
||||
CommandNames.SemanticDiagnosticsSync,
|
||||
{ file: jsFile.path }
|
||||
);
|
||||
const errorResult = <protocol.Diagnostic[]>session.executeCommand(getErrRequest).response;
|
||||
assert.isTrue(errorResult.length === 1);
|
||||
assert.equal(errorResult[0].code, Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap.code);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem non-existing directories listed in config file input array", () => {
|
||||
it("should be tolerated without crashing the server", () => {
|
||||
const configFile = {
|
||||
@@ -4079,156 +3552,6 @@ var x = 10;`
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem reload", () => {
|
||||
it("should work with temp file", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const tmp = {
|
||||
path: "/a/b/app.tmp",
|
||||
content: "const y = 42"
|
||||
};
|
||||
const host = createServerHost([f1, tmp]);
|
||||
const session = createSession(host);
|
||||
|
||||
// send open request
|
||||
session.executeCommand(<server.protocol.OpenRequest>{
|
||||
type: "request",
|
||||
command: "open",
|
||||
seq: 1,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
|
||||
// reload from tmp file
|
||||
session.executeCommand(<server.protocol.ReloadRequest>{
|
||||
type: "request",
|
||||
command: "reload",
|
||||
seq: 2,
|
||||
arguments: { file: f1.path, tmpfile: tmp.path }
|
||||
});
|
||||
|
||||
// verify content
|
||||
const projectServiice = session.getProjectService();
|
||||
const snap1 = projectServiice.getScriptInfo(f1.path)!.getSnapshot();
|
||||
assert.equal(getSnapshotText(snap1), tmp.content, "content should be equal to the content of temp file");
|
||||
|
||||
// reload from original file file
|
||||
session.executeCommand(<server.protocol.ReloadRequest>{
|
||||
type: "request",
|
||||
command: "reload",
|
||||
seq: 2,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
|
||||
// verify content
|
||||
const snap2 = projectServiice.getScriptInfo(f1.path)!.getSnapshot();
|
||||
assert.equal(getSnapshotText(snap2), f1.content, "content should be equal to the content of original file");
|
||||
|
||||
});
|
||||
|
||||
it("should work when script info doesnt have any project open", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const tmp = {
|
||||
path: "/a/b/app.tmp",
|
||||
content: "const y = 42"
|
||||
};
|
||||
const host = createServerHost([f1, tmp, libFile]);
|
||||
const session = createSession(host);
|
||||
const openContent = "let z = 1";
|
||||
// send open request
|
||||
session.executeCommandSeq(<server.protocol.OpenRequest>{
|
||||
command: server.protocol.CommandTypes.Open,
|
||||
arguments: { file: f1.path, fileContent: openContent }
|
||||
});
|
||||
|
||||
const projectService = session.getProjectService();
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
const info = projectService.getScriptInfo(f1.path)!;
|
||||
assert.isDefined(info);
|
||||
checkScriptInfoContents(openContent, "contents set during open request");
|
||||
|
||||
// send close request
|
||||
session.executeCommandSeq(<server.protocol.CloseRequest>{
|
||||
command: server.protocol.CommandTypes.Close,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// Can reload contents of the file when its not open and has no project
|
||||
// reload from temp file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path, tmpfile: tmp.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(tmp.content, "contents of temp file");
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// reload from own file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// Open file again without setting its content
|
||||
session.executeCommandSeq(<server.protocol.OpenRequest>{
|
||||
command: server.protocol.CommandTypes.Open,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(f1.content, "contents of file when opened without specifying contents");
|
||||
const snap = info.getSnapshot();
|
||||
|
||||
// send close request
|
||||
session.executeCommandSeq(<server.protocol.CloseRequest>{
|
||||
command: server.protocol.CommandTypes.Close,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
assert.strictEqual(info.getSnapshot(), snap);
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// reload from temp file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path, tmpfile: tmp.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(tmp.content, "contents of temp file");
|
||||
assert.notStrictEqual(info.getSnapshot(), snap);
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// reload from own file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
assert.notStrictEqual(info.getSnapshot(), snap);
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
function checkInferredProjectIsOrphan() {
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
assert.equal(info.containingProjects.length, 0);
|
||||
}
|
||||
|
||||
function checkScriptInfoAndProjects(contentsOfInfo: string, captionForContents: string) {
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
assert.strictEqual(projectService.getScriptInfo(f1.path), info);
|
||||
checkScriptInfoContents(contentsOfInfo, captionForContents);
|
||||
}
|
||||
|
||||
function checkScriptInfoContents(contentsOfInfo: string, captionForContents: string) {
|
||||
const snap = info.getSnapshot();
|
||||
assert.equal(getSnapshotText(snap), contentsOfInfo, "content should be equal to " + captionForContents);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem Inferred projects", () => {
|
||||
it("should support files without extensions", () => {
|
||||
const f = {
|
||||
|
||||
@@ -1,316 +0,0 @@
|
||||
namespace ts {
|
||||
import Tsc_WatchDirectory = TestFSWithWatch.Tsc_WatchDirectory;
|
||||
|
||||
export namespace tscWatch {
|
||||
describe("watchEnvironment:: tsc-watch with different polling/non polling options", () => {
|
||||
it("watchFile using dynamic priority polling", () => {
|
||||
const projectFolder = "/a/username/project";
|
||||
const file1: File = {
|
||||
path: `${projectFolder}/typescript.ts`,
|
||||
content: "var z = 10;"
|
||||
};
|
||||
const files = [file1, libFile];
|
||||
const environmentVariables = createMap<string>();
|
||||
environmentVariables.set("TSC_WATCHFILE", "DynamicPriorityPolling");
|
||||
const host = createWatchedSystem(files, { environmentVariables });
|
||||
const watch = createWatchOfFilesAndCompilerOptions([file1.path], host);
|
||||
|
||||
const initialProgram = watch();
|
||||
verifyProgram();
|
||||
|
||||
const mediumPollingIntervalThreshold = unchangedPollThresholds[PollingInterval.Medium];
|
||||
for (let index = 0; index < mediumPollingIntervalThreshold; index++) {
|
||||
// Transition libFile and file1 to low priority queue
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
assert.deepEqual(watch(), initialProgram);
|
||||
}
|
||||
|
||||
// Make a change to file
|
||||
file1.content = "var zz30 = 100;";
|
||||
host.reloadFS(files);
|
||||
|
||||
// This should detect change in the file
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
assert.deepEqual(watch(), initialProgram);
|
||||
|
||||
// Callbacks: medium priority + high priority queue and scheduled program update
|
||||
host.checkTimeoutQueueLengthAndRun(3);
|
||||
// During this timeout the file would be detected as unchanged
|
||||
let fileUnchangeDetected = 1;
|
||||
const newProgram = watch();
|
||||
assert.notStrictEqual(newProgram, initialProgram);
|
||||
|
||||
verifyProgram();
|
||||
const outputFile1 = changeExtension(file1.path, ".js");
|
||||
assert.isTrue(host.fileExists(outputFile1));
|
||||
assert.equal(host.readFile(outputFile1), file1.content + host.newLine);
|
||||
|
||||
const newThreshold = unchangedPollThresholds[PollingInterval.Low] + mediumPollingIntervalThreshold;
|
||||
for (; fileUnchangeDetected < newThreshold; fileUnchangeDetected++) {
|
||||
// For high + Medium/low polling interval
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
assert.deepEqual(watch(), newProgram);
|
||||
}
|
||||
|
||||
// Everything goes in high polling interval queue
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
assert.deepEqual(watch(), newProgram);
|
||||
|
||||
function verifyProgram() {
|
||||
checkProgramActualFiles(watch(), files.map(f => f.path));
|
||||
checkWatchedFiles(host, []);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ true);
|
||||
}
|
||||
});
|
||||
|
||||
describe("tsc-watch when watchDirectories implementation", () => {
|
||||
function verifyRenamingFileInSubFolder(tscWatchDirectory: Tsc_WatchDirectory) {
|
||||
const projectFolder = "/a/username/project";
|
||||
const projectSrcFolder = `${projectFolder}/src`;
|
||||
const configFile: File = {
|
||||
path: `${projectFolder}/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const file: File = {
|
||||
path: `${projectSrcFolder}/file1.ts`,
|
||||
content: ""
|
||||
};
|
||||
const programFiles = [file, libFile];
|
||||
const files = [file, configFile, libFile];
|
||||
const environmentVariables = createMap<string>();
|
||||
environmentVariables.set("TSC_WATCHDIRECTORY", tscWatchDirectory);
|
||||
const host = createWatchedSystem(files, { environmentVariables });
|
||||
const watch = createWatchOfConfigFile(configFile.path, host);
|
||||
const projectFolders = [projectFolder, projectSrcFolder, `${projectFolder}/node_modules/@types`];
|
||||
// Watching files config file, file, lib file
|
||||
const expectedWatchedFiles = files.map(f => f.path);
|
||||
const expectedWatchedDirectories = tscWatchDirectory === Tsc_WatchDirectory.NonRecursiveWatchDirectory ? projectFolders : emptyArray;
|
||||
if (tscWatchDirectory === Tsc_WatchDirectory.WatchFile) {
|
||||
expectedWatchedFiles.push(...projectFolders);
|
||||
}
|
||||
|
||||
verifyProgram(checkOutputErrorsInitial);
|
||||
|
||||
// Rename the file:
|
||||
file.path = file.path.replace("file1.ts", "file2.ts");
|
||||
expectedWatchedFiles[0] = file.path;
|
||||
host.reloadFS(files);
|
||||
if (tscWatchDirectory === Tsc_WatchDirectory.DynamicPolling) {
|
||||
// With dynamic polling the fs change would be detected only by running timeouts
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
}
|
||||
// Delayed update program
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
verifyProgram(checkOutputErrorsIncremental);
|
||||
|
||||
function verifyProgram(checkOutputErrors: (host: WatchedSystem, errors: ReadonlyArray<Diagnostic>) => void) {
|
||||
checkProgramActualFiles(watch(), programFiles.map(f => f.path));
|
||||
checkOutputErrors(host, emptyArray);
|
||||
|
||||
const outputFile = changeExtension(file.path, ".js");
|
||||
assert(host.fileExists(outputFile));
|
||||
assert.equal(host.readFile(outputFile), file.content);
|
||||
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
|
||||
|
||||
// Watching config file, file, lib file and directories
|
||||
checkWatchedFilesDetailed(host, expectedWatchedFiles, 1);
|
||||
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, 1, /*recursive*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
it("uses watchFile when renaming file in subfolder", () => {
|
||||
verifyRenamingFileInSubFolder(Tsc_WatchDirectory.WatchFile);
|
||||
});
|
||||
|
||||
it("uses non recursive watchDirectory when renaming file in subfolder", () => {
|
||||
verifyRenamingFileInSubFolder(Tsc_WatchDirectory.NonRecursiveWatchDirectory);
|
||||
});
|
||||
|
||||
it("uses non recursive dynamic polling when renaming file in subfolder", () => {
|
||||
verifyRenamingFileInSubFolder(Tsc_WatchDirectory.DynamicPolling);
|
||||
});
|
||||
|
||||
it("when there are symlinks to folders in recursive folders", () => {
|
||||
const cwd = "/home/user/projects/myproject";
|
||||
const file1: File = {
|
||||
path: `${cwd}/src/file.ts`,
|
||||
content: `import * as a from "a"`
|
||||
};
|
||||
const tsconfig: File = {
|
||||
path: `${cwd}/tsconfig.json`,
|
||||
content: `{ "compilerOptions": { "extendedDiagnostics": true, "traceResolution": true }}`
|
||||
};
|
||||
const realA: File = {
|
||||
path: `${cwd}/node_modules/reala/index.d.ts`,
|
||||
content: `export {}`
|
||||
};
|
||||
const realB: File = {
|
||||
path: `${cwd}/node_modules/realb/index.d.ts`,
|
||||
content: `export {}`
|
||||
};
|
||||
const symLinkA: SymLink = {
|
||||
path: `${cwd}/node_modules/a`,
|
||||
symLink: `${cwd}/node_modules/reala`
|
||||
};
|
||||
const symLinkB: SymLink = {
|
||||
path: `${cwd}/node_modules/b`,
|
||||
symLink: `${cwd}/node_modules/realb`
|
||||
};
|
||||
const symLinkBInA: SymLink = {
|
||||
path: `${cwd}/node_modules/reala/node_modules/b`,
|
||||
symLink: `${cwd}/node_modules/b`
|
||||
};
|
||||
const symLinkAInB: SymLink = {
|
||||
path: `${cwd}/node_modules/realb/node_modules/a`,
|
||||
symLink: `${cwd}/node_modules/a`
|
||||
};
|
||||
const files = [file1, tsconfig, realA, realB, symLinkA, symLinkB, symLinkBInA, symLinkAInB];
|
||||
const environmentVariables = createMap<string>();
|
||||
environmentVariables.set("TSC_WATCHDIRECTORY", Tsc_WatchDirectory.NonRecursiveWatchDirectory);
|
||||
const host = createWatchedSystem(files, { environmentVariables, currentDirectory: cwd });
|
||||
createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
|
||||
checkWatchedDirectories(host, [cwd, `${cwd}/node_modules`, `${cwd}/node_modules/@types`, `${cwd}/node_modules/reala`, `${cwd}/node_modules/realb`,
|
||||
`${cwd}/node_modules/reala/node_modules`, `${cwd}/node_modules/realb/node_modules`, `${cwd}/src`], /*recursive*/ false);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export namespace projectSystem {
|
||||
describe("watchEnvironment:: tsserverProjectSystem watchDirectories implementation", () => {
|
||||
function verifyCompletionListWithNewFileInSubFolder(tscWatchDirectory: Tsc_WatchDirectory) {
|
||||
const projectFolder = "/a/username/project";
|
||||
const projectSrcFolder = `${projectFolder}/src`;
|
||||
const configFile: File = {
|
||||
path: `${projectFolder}/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const index: File = {
|
||||
path: `${projectSrcFolder}/index.ts`,
|
||||
content: `import {} from "./"`
|
||||
};
|
||||
const file1: File = {
|
||||
path: `${projectSrcFolder}/file1.ts`,
|
||||
content: ""
|
||||
};
|
||||
|
||||
const files = [index, file1, configFile, libFile];
|
||||
const fileNames = files.map(file => file.path);
|
||||
// All closed files(files other than index), project folder, project/src folder and project/node_modules/@types folder
|
||||
const expectedWatchedFiles = arrayToMap(fileNames.slice(1), s => s, () => 1);
|
||||
const expectedWatchedDirectories = createMap<number>();
|
||||
const mapOfDirectories = tscWatchDirectory === Tsc_WatchDirectory.NonRecursiveWatchDirectory ?
|
||||
expectedWatchedDirectories :
|
||||
tscWatchDirectory === Tsc_WatchDirectory.WatchFile ?
|
||||
expectedWatchedFiles :
|
||||
createMap();
|
||||
// For failed resolution lookup and tsconfig files => cached so only watched only once
|
||||
mapOfDirectories.set(projectFolder, 1);
|
||||
// Through above recursive watches
|
||||
mapOfDirectories.set(projectSrcFolder, 1);
|
||||
// node_modules/@types folder
|
||||
mapOfDirectories.set(`${projectFolder}/${nodeModulesAtTypes}`, 1);
|
||||
const expectedCompletions = ["file1"];
|
||||
const completionPosition = index.content.lastIndexOf('"');
|
||||
const environmentVariables = createMap<string>();
|
||||
environmentVariables.set("TSC_WATCHDIRECTORY", tscWatchDirectory);
|
||||
const host = createServerHost(files, { environmentVariables });
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(index.path);
|
||||
|
||||
const project = Debug.assertDefined(projectService.configuredProjects.get(configFile.path));
|
||||
verifyProjectAndCompletions();
|
||||
|
||||
// Add file2
|
||||
const file2: File = {
|
||||
path: `${projectSrcFolder}/file2.ts`,
|
||||
content: ""
|
||||
};
|
||||
files.push(file2);
|
||||
fileNames.push(file2.path);
|
||||
expectedWatchedFiles.set(file2.path, 1);
|
||||
expectedCompletions.push("file2");
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
assert.equal(projectService.configuredProjects.get(configFile.path), project);
|
||||
verifyProjectAndCompletions();
|
||||
|
||||
function verifyProjectAndCompletions() {
|
||||
const completions = project.getLanguageService().getCompletionsAtPosition(index.path, completionPosition, { includeExternalModuleExports: false, includeInsertTextCompletions: false })!;
|
||||
checkArray("Completion Entries", completions.entries.map(e => e.name), expectedCompletions);
|
||||
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
|
||||
|
||||
checkWatchedFilesDetailed(host, expectedWatchedFiles);
|
||||
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, /*recursive*/ false);
|
||||
checkProjectActualFiles(project, fileNames);
|
||||
}
|
||||
}
|
||||
|
||||
it("uses watchFile when file is added to subfolder, completion list has new file", () => {
|
||||
verifyCompletionListWithNewFileInSubFolder(Tsc_WatchDirectory.WatchFile);
|
||||
});
|
||||
|
||||
it("uses non recursive watchDirectory when file is added to subfolder, completion list has new file", () => {
|
||||
verifyCompletionListWithNewFileInSubFolder(Tsc_WatchDirectory.NonRecursiveWatchDirectory);
|
||||
});
|
||||
|
||||
it("uses dynamic polling when file is added to subfolder, completion list has new file", () => {
|
||||
verifyCompletionListWithNewFileInSubFolder(Tsc_WatchDirectory.DynamicPolling);
|
||||
});
|
||||
});
|
||||
|
||||
describe("watchEnvironment:: tsserverProjectSystem Watched recursive directories with windows style file system", () => {
|
||||
function verifyWatchedDirectories(rootedPath: string, useProjectAtRoot: boolean) {
|
||||
const root = useProjectAtRoot ? rootedPath : `${rootedPath}myfolder/allproject/`;
|
||||
const configFile: File = {
|
||||
path: root + "project/tsconfig.json",
|
||||
content: "{}"
|
||||
};
|
||||
const file1: File = {
|
||||
path: root + "project/file1.ts",
|
||||
content: "let x = 10;"
|
||||
};
|
||||
const file2: File = {
|
||||
path: root + "project/file2.ts",
|
||||
content: "let y = 10;"
|
||||
};
|
||||
const files = [configFile, file1, file2, libFile];
|
||||
const host = createServerHost(files, { useWindowsStylePaths: true });
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(file1.path);
|
||||
const project = projectService.configuredProjects.get(configFile.path)!;
|
||||
assert.isDefined(project);
|
||||
const winsowsStyleLibFilePath = "c:/" + libFile.path.substring(1);
|
||||
checkProjectActualFiles(project, files.map(f => f === libFile ? winsowsStyleLibFilePath : f.path));
|
||||
checkWatchedFiles(host, mapDefined(files, f => f === libFile ? winsowsStyleLibFilePath : f === file1 ? undefined : f.path));
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [
|
||||
root + "project",
|
||||
root + "project/node_modules/@types"
|
||||
].concat(useProjectAtRoot ? [] : [root + nodeModulesAtTypes]), /*recursive*/ true);
|
||||
}
|
||||
|
||||
function verifyRootedDirectoryWatch(rootedPath: string) {
|
||||
it("When project is in rootFolder of style c:/", () => {
|
||||
verifyWatchedDirectories(rootedPath, /*useProjectAtRoot*/ true);
|
||||
});
|
||||
|
||||
it("When files at some folder other than root", () => {
|
||||
verifyWatchedDirectories(rootedPath, /*useProjectAtRoot*/ false);
|
||||
});
|
||||
}
|
||||
|
||||
describe("for rootFolder of style c:/", () => {
|
||||
verifyRootedDirectoryWatch("c:/");
|
||||
});
|
||||
|
||||
describe("for rootFolder of style c:/users/username", () => {
|
||||
verifyRootedDirectoryWatch("c:/users/username/");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user