Merge branch 'master' into baselining

This commit is contained in:
Sheetal Nandi
2019-09-24 13:26:05 -07:00
186 changed files with 10811 additions and 2397 deletions
+2
View File
@@ -146,6 +146,8 @@
"unittests/tsserver/occurences.ts",
"unittests/tsserver/openFile.ts",
"unittests/tsserver/projectErrors.ts",
"unittests/tsserver/projectReferenceCompileOnSave.ts",
"unittests/tsserver/projectReferenceErrors.ts",
"unittests/tsserver/projectReferences.ts",
"unittests/tsserver/projects.ts",
"unittests/tsserver/refactors.ts",
+112 -93
View File
@@ -2,18 +2,12 @@ namespace ts.tscWatch {
import projectsLocation = TestFSWithWatch.tsbuildProjectsLocation;
import getFilePathInProject = TestFSWithWatch.getTsBuildProjectFilePath;
import getFileFromProject = TestFSWithWatch.getTsBuildProjectFile;
type TsBuildWatchSystem = WatchedSystem & { writtenFiles: Map<true>; };
type TsBuildWatchSystem = TestFSWithWatch.TestServerHostTrackingWrittenFiles;
function createTsBuildWatchSystem(fileOrFolderList: readonly TestFSWithWatch.FileOrFolderOrSymLink[], params?: TestFSWithWatch.TestServerHostCreationParameters) {
const host = createWatchedSystem(fileOrFolderList, params) as TsBuildWatchSystem;
const originalWriteFile = host.writeFile;
host.writtenFiles = createMap<true>();
host.writeFile = (fileName, content) => {
originalWriteFile.call(host, fileName, content);
const path = host.toFullPath(fileName);
host.writtenFiles.set(path, true);
};
return host;
return TestFSWithWatch.changeToHostTrackingWrittenFiles(
createWatchedSystem(fileOrFolderList, params)
);
}
export function createSolutionBuilder(system: WatchedSystem, rootNames: readonly string[], defaultOptions?: BuildOptions) {
@@ -710,8 +704,8 @@ let x: string = 10;`);
const coreIndexDts = projectFileName(SubProject.core, "index.d.ts");
const coreAnotherModuleDts = projectFileName(SubProject.core, "anotherModule.d.ts");
const logicIndexDts = projectFileName(SubProject.logic, "index.d.ts");
const expectedWatchedFiles = () => [core[0], logic[0], ...tests, libFile].map(f => f.path).concat([coreIndexDts, coreAnotherModuleDts, logicIndexDts].map(f => f.toLowerCase()));
const expectedWatchedDirectoriesRecursive = projectSystem.getTypeRootsFromLocation(projectPath(SubProject.tests));
const expectedProjectFiles = () => [libFile, ...tests, ...logic.slice(1), ...core.slice(1, core.length - 1)].map(f => f.path);
const expectedProgramFiles = () => [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, logicIndexDts];
function createSolutionAndWatchMode() {
@@ -723,12 +717,19 @@ let x: string = 10;`);
}
function verifyWatches(host: TsBuildWatchSystem, withTsserver?: boolean) {
verifyWatchesOfProject(host, withTsserver ? expectedWatchedFiles().filter(f => f !== tests[1].path.toLowerCase()) : expectedWatchedFiles(), expectedWatchedDirectoriesRecursive);
verifyWatchesOfProject(
host,
withTsserver ?
[...core.slice(0, core.length - 1), ...logic, tests[0], libFile].map(f => f.path.toLowerCase()) :
[core[0], logic[0], ...tests, libFile].map(f => f.path).concat([coreIndexDts, coreAnotherModuleDts, logicIndexDts].map(f => f.toLowerCase())),
expectedWatchedDirectoriesRecursive
);
}
function verifyScenario(
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void,
expectedFilesAfterEdit: () => readonly string[]
expectedProgramFilesAfterEdit: () => readonly string[],
expectedProjectFilesAfterEdit: () => readonly string[]
) {
it("with tsc-watch", () => {
const { host, solutionBuilder, watch } = createSolutionAndWatchMode();
@@ -737,7 +738,7 @@ let x: string = 10;`);
host.checkTimeoutQueueLengthAndRun(1);
checkOutputErrorsIncremental(host, emptyArray);
checkProgramActualFiles(watch(), expectedFilesAfterEdit());
checkProgramActualFiles(watch(), expectedProgramFilesAfterEdit());
});
@@ -747,7 +748,7 @@ let x: string = 10;`);
edit(host, solutionBuilder);
host.checkTimeoutQueueLengthAndRun(2);
checkProjectActualFiles(service, tests[0].path, [tests[0].path, ...expectedFilesAfterEdit()]);
checkProjectActualFiles(service, tests[0].path, expectedProjectFilesAfterEdit());
});
}
@@ -777,7 +778,7 @@ function foo() {
// not ideal, but currently because of d.ts but no new file is written
// There will be timeout queued even though file contents are same
}, expectedProgramFiles);
}, expectedProgramFiles, expectedProjectFiles);
});
describe("non local edit in ts file, rebuilds in watch compilation", () => {
@@ -787,7 +788,7 @@ export function gfoo() {
}`);
solutionBuilder.invalidateProject(logic[0].path.toLowerCase() as ResolvedConfigFilePath);
solutionBuilder.buildNextInvalidatedProject();
}, expectedProgramFiles);
}, expectedProgramFiles, expectedProjectFiles);
});
describe("change in project reference config file builds correctly", () => {
@@ -798,7 +799,7 @@ export function gfoo() {
}));
solutionBuilder.invalidateProject(logic[0].path.toLowerCase() as ResolvedConfigFilePath, ConfigFileProgramReloadLevel.Full);
solutionBuilder.buildNextInvalidatedProject();
}, () => [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, projectFilePath(SubProject.logic, "decls/index.d.ts")]);
}, () => [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, projectFilePath(SubProject.logic, "decls/index.d.ts")], expectedProjectFiles);
});
});
@@ -888,7 +889,9 @@ export function gfoo() {
const aDts = dtsFile(multiFolder ? "a/index" : "a"), bDts = dtsFile(multiFolder ? "b/index" : "b");
const expectedFiles = [jsFile(multiFolder ? "a/index" : "a"), aDts, jsFile(multiFolder ? "b/index" : "b"), bDts, jsFile(multiFolder ? "c/index" : "c")];
const expectedProgramFiles = [cTs.path, libFile.path, aDts, refs.path, bDts];
const expectedProjectFiles = [cTs.path, libFile.path, aTs.path, refs.path, bTs.path];
const expectedWatchedFiles = expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase());
const expectedProjectWatchedFiles = expectedProjectFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase());
const expectedWatchedDirectories = multiFolder ? [
getProjectPath(project).toLowerCase() // watches for directories created for resolution of b
] : emptyArray;
@@ -926,22 +929,29 @@ export function gfoo() {
}
function verifyProject(host: TsBuildWatchSystem, service: projectSystem.TestProjectService, orphanInfos?: readonly string[]) {
verifyServerState(host, service, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos);
verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos });
}
function verifyServerState(
host: TsBuildWatchSystem,
service: projectSystem.TestProjectService,
expectedProgramFiles: readonly string[],
expectedWatchedFiles: readonly string[],
expectedWatchedDirectoriesRecursive: readonly string[],
orphanInfos?: readonly string[]) {
checkProjectActualFiles(service, cTsconfig.path, expectedProgramFiles.concat(cTsconfig.path));
const watchedFiles = expectedWatchedFiles.filter(f => f !== cTs.path.toLowerCase());
if (orphanInfos) {
interface VerifyServerState {
host: TsBuildWatchSystem;
service: projectSystem.TestProjectService;
expectedProjectFiles: readonly string[];
expectedProjectWatchedFiles: readonly string[];
expectedWatchedDirectoriesRecursive: readonly string[];
orphanInfos?: readonly string[];
}
function verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos }: VerifyServerState) {
checkProjectActualFiles(service, cTsconfig.path, expectedProjectFiles.concat(cTsconfig.path));
const watchedFiles = expectedProjectWatchedFiles.filter(f => f !== cTs.path.toLowerCase());
const actualOrphan = arrayFrom(mapDefinedIterator(
service.filenameToScriptInfo.values(),
v => v.containingProjects.length === 0 ? v.fileName : undefined
));
assert.equal(actualOrphan.length, orphanInfos ? orphanInfos.length : 0, `Orphans found: ${JSON.stringify(actualOrphan, /*replacer*/ undefined, " ")}`);
if (orphanInfos && orphanInfos.length) {
for (const orphan of orphanInfos) {
const info = service.getScriptInfoForPath(orphan as Path);
assert.isDefined(info);
assert.isDefined(info, `${orphan} expected to be present. Actual: ${JSON.stringify(actualOrphan, /*replacer*/ undefined, " ")}`);
assert.equal(info!.containingProjects.length, 0);
watchedFiles.push(orphan);
}
@@ -949,16 +959,20 @@ export function gfoo() {
verifyWatchesOfProject(host, watchedFiles, expectedWatchedDirectoriesRecursive, expectedWatchedDirectories);
}
function verifyScenario(
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void,
expectedEditErrors: readonly string[],
expectedProgramFiles: readonly string[],
expectedWatchedFiles: readonly string[],
expectedWatchedDirectoriesRecursive: readonly string[],
dependencies: readonly [string, readonly string[]][],
revert?: (host: TsBuildWatchSystem) => void,
orphanInfosAfterEdit?: readonly string[],
orphanInfosAfterRevert?: readonly string[]) {
interface VerifyScenario {
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void;
expectedEditErrors: readonly string[];
expectedProgramFiles: readonly string[];
expectedProjectFiles: readonly string[];
expectedWatchedFiles: readonly string[];
expectedProjectWatchedFiles: readonly string[];
expectedWatchedDirectoriesRecursive: readonly string[];
dependencies: readonly [string, readonly string[]][];
revert?: (host: TsBuildWatchSystem) => void;
orphanInfosAfterEdit?: readonly string[];
orphanInfosAfterRevert?: readonly string[];
}
function verifyScenario({ edit, expectedEditErrors, expectedProgramFiles, expectedProjectFiles, expectedWatchedFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, dependencies, revert, orphanInfosAfterEdit, orphanInfosAfterRevert }: VerifyScenario) {
it("with tsc-watch", () => {
const { host, solutionBuilder, watch } = createSolutionAndWatchMode();
@@ -985,7 +999,7 @@ export function gfoo() {
edit(host, solutionBuilder);
host.checkTimeoutQueueLengthAndRun(2);
verifyServerState(host, service, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfosAfterEdit);
verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos: orphanInfosAfterEdit });
if (revert) {
revert(host);
@@ -1010,20 +1024,21 @@ export function gfoo() {
});
describe("non local edit updates the program and watch correctly", () => {
verifyScenario(
(host, solutionBuilder) => {
verifyScenario({
edit: (host, solutionBuilder) => {
// edit
host.writeFile(bTs.path, `${bTs.content}
export function gfoo() {
}`);
solutionBuilder.invalidateProject(bTsconfig.path.toLowerCase() as ResolvedConfigFilePath);
host.writeFile(bTs.path, `${bTs.content}\nexport function gfoo() {\n}`);
solutionBuilder.invalidateProject((bTsconfig.path.toLowerCase() as ResolvedConfigFilePath));
solutionBuilder.buildNextInvalidatedProject();
},
emptyArray,
expectedEditErrors: emptyArray,
expectedProgramFiles,
expectedProjectFiles,
expectedWatchedFiles,
expectedProjectWatchedFiles,
expectedWatchedDirectoriesRecursive,
defaultDependencies);
dependencies: defaultDependencies
});
});
describe("edit on config file", () => {
@@ -1032,30 +1047,32 @@ export function gfoo() {
path: getFilePathInProject(project, "nrefs/a.d.ts"),
content: refs.content
};
verifyScenario(
host => {
verifyScenario({
edit: host => {
const cTsConfigJson = JSON.parse(cTsconfig.content);
host.ensureFileOrFolder(nrefs);
cTsConfigJson.compilerOptions.paths = { "@ref/*": nrefsPath };
host.writeFile(cTsconfig.path, JSON.stringify(cTsConfigJson));
},
emptyArray,
expectedProgramFiles.map(nrefReplacer),
expectedWatchedFiles.map(nrefReplacer),
expectedWatchedDirectoriesRecursive.map(nrefReplacer),
[
expectedEditErrors: emptyArray,
expectedProgramFiles: expectedProgramFiles.map(nrefReplacer),
expectedProjectFiles: expectedProjectFiles.map(nrefReplacer),
expectedWatchedFiles: expectedWatchedFiles.map(nrefReplacer),
expectedProjectWatchedFiles: expectedProjectWatchedFiles.map(nrefReplacer),
expectedWatchedDirectoriesRecursive: expectedWatchedDirectoriesRecursive.map(nrefReplacer),
dependencies: [
[aDts, [aDts]],
[bDts, [bDts, aDts]],
[nrefs.path, [nrefs.path]],
[cTs.path, [cTs.path, nrefs.path, bDts]]
],
// revert the update
host => host.writeFile(cTsconfig.path, cTsconfig.content),
revert: host => host.writeFile(cTsconfig.path, cTsconfig.content),
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
[refs.path.toLowerCase()],
orphanInfosAfterEdit: [refs.path.toLowerCase()],
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
[nrefs.path.toLowerCase()]
);
orphanInfosAfterRevert: [nrefs.path.toLowerCase()]
});
});
describe("edit in referenced config file", () => {
@@ -1064,82 +1081,84 @@ export function gfoo() {
content: "export declare class A {}"
};
const expectedProgramFiles = [cTs.path, bDts, nrefs.path, refs.path, libFile.path];
const expectedProjectFiles = [cTs.path, bTs.path, nrefs.path, refs.path, libFile.path];
const [, ...expectedWatchedDirectoriesRecursiveWithoutA] = expectedWatchedDirectoriesRecursive; // Not looking in a folder for resolution in multi folder scenario
verifyScenario(
host => {
verifyScenario({
edit: host => {
const bTsConfigJson = JSON.parse(bTsconfig.content);
host.ensureFileOrFolder(nrefs);
bTsConfigJson.compilerOptions.paths = { "@ref/*": nrefsPath };
host.writeFile(bTsconfig.path, JSON.stringify(bTsConfigJson));
},
emptyArray,
expectedEditErrors: emptyArray,
expectedProgramFiles,
expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase()),
(multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive).concat(getFilePathInProject(project, "nrefs").toLowerCase()),
[
expectedProjectFiles,
expectedWatchedFiles: expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase()),
expectedProjectWatchedFiles: expectedProjectFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase()),
expectedWatchedDirectoriesRecursive: (multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive).concat(getFilePathInProject(project, "nrefs").toLowerCase()),
dependencies: [
[nrefs.path, [nrefs.path]],
[bDts, [bDts, nrefs.path]],
[refs.path, [refs.path]],
[cTs.path, [cTs.path, refs.path, bDts]],
],
// revert the update
host => host.writeFile(bTsconfig.path, bTsconfig.content),
revert: host => host.writeFile(bTsconfig.path, bTsconfig.content),
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
[aDts.toLowerCase()],
orphanInfosAfterEdit: [aTs.path.toLowerCase()],
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
[nrefs.path.toLowerCase()]
);
orphanInfosAfterRevert: [nrefs.path.toLowerCase()]
});
});
describe("deleting referenced config file", () => {
const expectedProgramFiles = [cTs.path, bTs.path, refs.path, libFile.path];
const expectedWatchedFiles = expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path).map(s => s.toLowerCase());
const [, ...expectedWatchedDirectoriesRecursiveWithoutA] = expectedWatchedDirectoriesRecursive; // Not looking in a folder for resolution in multi folder scenario
// Resolutions should change now
// Should map to b.ts instead with options from our own config
verifyScenario(
host => host.deleteFile(bTsconfig.path),
[
verifyScenario({
edit: host => host.deleteFile(bTsconfig.path),
expectedEditErrors: [
`${multiFolder ? "c/tsconfig.json" : "tsconfig.c.json"}(9,21): error TS6053: File '/user/username/projects/transitiveReferences/${multiFolder ? "b" : "tsconfig.b.json"}' not found.\n`
],
expectedProgramFiles,
expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path).map(s => s.toLowerCase()),
multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive,
[
expectedProjectFiles: expectedProgramFiles,
expectedWatchedFiles,
expectedProjectWatchedFiles: expectedWatchedFiles,
expectedWatchedDirectoriesRecursive: multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive,
dependencies: [
[bTs.path, [bTs.path, refs.path]],
[refs.path, [refs.path]],
[cTs.path, [cTs.path, refs.path, bTs.path]],
],
// revert the update
host => host.writeFile(bTsconfig.path, bTsconfig.content),
revert: host => host.writeFile(bTsconfig.path, bTsconfig.content),
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
[bDts.toLowerCase(), aDts.toLowerCase(), aTsconfig.path.toLowerCase()],
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
[bTs.path.toLowerCase()]
);
orphanInfosAfterEdit: [aTs.path.toLowerCase(), aTsconfig.path.toLowerCase()],
});
});
describe("deleting transitively referenced config file", () => {
verifyScenario(
host => host.deleteFile(aTsconfig.path),
[
verifyScenario({
edit: host => host.deleteFile(aTsconfig.path),
expectedEditErrors: [
`${multiFolder ? "b/tsconfig.json" : "tsconfig.b.json"}(10,21): error TS6053: File '/user/username/projects/transitiveReferences/${multiFolder ? "a" : "tsconfig.a.json"}' not found.\n`
],
expectedProgramFiles.map(s => s.replace(aDts, aTs.path)),
expectedWatchedFiles.map(s => s.replace(aDts.toLowerCase(), aTs.path.toLocaleLowerCase())),
expectedProgramFiles: expectedProgramFiles.map(s => s.replace(aDts, aTs.path)),
expectedProjectFiles,
expectedWatchedFiles: expectedWatchedFiles.map(s => s.replace(aDts.toLowerCase(), aTs.path.toLocaleLowerCase())),
expectedProjectWatchedFiles,
expectedWatchedDirectoriesRecursive,
[
dependencies: [
[aTs.path, [aTs.path]],
[bDts, [bDts, aTs.path]],
[refs.path, [refs.path]],
[cTs.path, [cTs.path, refs.path, bDts]],
],
// revert the update
host => host.writeFile(aTsconfig.path, aTsconfig.content),
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
[aDts.toLowerCase()],
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
[aTs.path.toLowerCase()]
);
revert: host => host.writeFile(aTsconfig.path, aTsconfig.content),
});
});
}
@@ -179,7 +179,7 @@ namespace ts.projectSystem {
}
function verifyUserTsConfigProject(session: TestSession) {
checkProjectActualFiles(session.getProjectService().configuredProjects.get(userTsconfig.path)!, [userTs.path, aDts.path, userTsconfig.path]);
checkProjectActualFiles(session.getProjectService().configuredProjects.get(userTsconfig.path)!, [userTs.path, aTs.path, userTsconfig.path]);
}
it("goToDefinition", () => {
@@ -450,6 +450,13 @@ namespace ts.projectSystem {
name: "function f(): void",
},
references: [
makeReferenceEntry({
file: aTs,
text: "f",
options: { index: 1 },
contextText: "function f() {}",
isDefinition: true
}),
{
fileName: bTs.path,
isDefinition: false,
@@ -457,13 +464,6 @@ namespace ts.projectSystem {
isWriteAccess: false,
textSpan: { start: 0, length: 1 },
},
makeReferenceEntry({
file: aTs,
text: "f",
options: { index: 1 },
contextText: "function f() {}",
isDefinition: true
})
],
}
]);
@@ -73,44 +73,64 @@ namespace ts.projectSystem {
verifyEvent(project, `Change in config file detected`);
});
it("when opening original location project", () => {
const aDTs: File = {
path: `${projectRoot}/a/a.d.ts`,
content: `export declare class A {
describe("when opening original location project", () => {
it("with project references", () => {
verify();
});
it("when disableSourceOfProjectReferenceRedirect is true", () => {
verify(/*disableSourceOfProjectReferenceRedirect*/ true);
});
function verify(disableSourceOfProjectReferenceRedirect?: true) {
const aDTs: File = {
path: `${projectRoot}/a/a.d.ts`,
content: `export declare class A {
}
//# sourceMappingURL=a.d.ts.map
`
};
const aDTsMap: File = {
path: `${projectRoot}/a/a.d.ts.map`,
content: `{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["./a.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;CAAI"}`
};
const bTs: File = {
path: bTsPath,
content: `import {A} from "../a/a"; new A();`
};
const configB: File = {
path: configBPath,
content: JSON.stringify({
references: [{ path: "../a" }]
})
};
};
const aDTsMap: File = {
path: `${projectRoot}/a/a.d.ts.map`,
content: `{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["./a.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;CAAI"}`
};
const bTs: File = {
path: bTsPath,
content: `import {A} from "../a/a"; new A();`
};
const configB: File = {
path: configBPath,
content: JSON.stringify({
...(disableSourceOfProjectReferenceRedirect && {
compilerOptions: {
disableSourceOfProjectReferenceRedirect
}
}),
references: [{ path: "../a" }]
})
};
const { service, session, verifyEventWithOpenTs, verifyEvent } = createSessionToVerifyEvent(files.concat(aDTs, aDTsMap, bTs, configB));
verifyEventWithOpenTs(bTs, configB.path, 1);
const { service, session, verifyEventWithOpenTs, verifyEvent } = createSessionToVerifyEvent(files.concat(aDTs, aDTsMap, bTs, configB));
verifyEventWithOpenTs(bTs, configB.path, 1);
session.executeCommandSeq<protocol.ReferencesRequest>({
command: protocol.CommandTypes.References,
arguments: {
file: bTs.path,
...protocolLocationFromSubstring(bTs.content, "A()")
}
});
session.executeCommandSeq<protocol.ReferencesRequest>({
command: protocol.CommandTypes.References,
arguments: {
file: bTs.path,
...protocolLocationFromSubstring(bTs.content, "A()")
}
});
checkNumberOfProjects(service, { configuredProjects: 2 });
const project = service.configuredProjects.get(configA.path)!;
assert.isDefined(project);
verifyEvent(project, `Creating project for original file: ${aTs.path} for location: ${aDTs.path}`);
checkNumberOfProjects(service, { configuredProjects: 2 });
const project = service.configuredProjects.get(configA.path)!;
assert.isDefined(project);
verifyEvent(
project,
disableSourceOfProjectReferenceRedirect ?
`Creating project for original file: ${aTs.path} for location: ${aDTs.path}` :
`Creating project for original file: ${aTs.path}`
);
}
});
describe("with external projects and config files ", () => {
+3 -3
View File
@@ -491,8 +491,8 @@ namespace ts.projectSystem {
checkArray("Open files", arrayFrom(projectService.openFiles.keys(), path => projectService.getScriptInfoForPath(path as Path)!.fileName), expectedFiles.map(file => file.path));
}
export function checkScriptInfos(projectService: server.ProjectService, expectedFiles: readonly string[]) {
checkArray("ScriptInfos files", arrayFrom(projectService.filenameToScriptInfo.values(), info => info.fileName), expectedFiles);
export function checkScriptInfos(projectService: server.ProjectService, expectedFiles: readonly string[], additionInfo?: string) {
checkArray(`ScriptInfos files: ${additionInfo || ""}`, arrayFrom(projectService.filenameToScriptInfo.values(), info => info.fileName), expectedFiles);
}
export function protocolLocationFromSubstring(str: string, substring: string): protocol.Location {
@@ -501,7 +501,7 @@ namespace ts.projectSystem {
return protocolToLocation(str)(start);
}
function protocolToLocation(text: string): (pos: number) => protocol.Location {
export function protocolToLocation(text: string): (pos: number) => protocol.Location {
const lineStarts = computeLineStarts(text);
return pos => {
const x = computeLineAndCharacterOfPosition(lineStarts, pos);
@@ -0,0 +1,410 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: with project references and compile on save", () => {
const projectLocation = "/user/username/projects/myproject";
const dependecyLocation = `${projectLocation}/dependency`;
const usageLocation = `${projectLocation}/usage`;
const dependencyTs: File = {
path: `${dependecyLocation}/fns.ts`,
content: `export function fn1() { }
export function fn2() { }
`
};
const dependencyConfig: File = {
path: `${dependecyLocation}/tsconfig.json`,
content: JSON.stringify({
compilerOptions: { composite: true, declarationDir: "../decls" },
compileOnSave: true
})
};
const usageTs: File = {
path: `${usageLocation}/usage.ts`,
content: `import {
fn1,
fn2,
} from '../decls/fns'
fn1();
fn2();
`
};
const usageConfig: File = {
path: `${usageLocation}/tsconfig.json`,
content: JSON.stringify({
compileOnSave: true,
references: [{ path: "../dependency" }]
})
};
interface VerifySingleScenarioWorker extends VerifySingleScenario {
withProject: boolean;
}
function verifySingleScenarioWorker({
withProject, scenario, openFiles, requestArgs, change, expectedResult
}: VerifySingleScenarioWorker) {
it(scenario, () => {
const host = TestFSWithWatch.changeToHostTrackingWrittenFiles(
createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile])
);
const session = createSession(host);
openFilesForSession(openFiles(), session);
const reqArgs = requestArgs();
const {
expectedAffected,
expectedEmit: { expectedEmitSuccess, expectedFiles },
expectedEmitOutput
} = expectedResult(withProject);
if (change) {
session.executeCommandSeq<protocol.CompileOnSaveAffectedFileListRequest>({
command: protocol.CommandTypes.CompileOnSaveAffectedFileList,
arguments: { file: dependencyTs.path }
});
const { file, insertString } = change();
if (session.getProjectService().openFiles.has(file.path)) {
const toLocation = protocolToLocation(file.content);
const location = toLocation(file.content.length);
session.executeCommandSeq<protocol.ChangeRequest>({
command: protocol.CommandTypes.Change,
arguments: {
file: file.path,
...location,
endLine: location.line,
endOffset: location.offset,
insertString
}
});
}
else {
host.writeFile(file.path, `${file.content}${insertString}`);
}
host.writtenFiles.clear();
}
const args = withProject ? reqArgs : { file: reqArgs.file };
// Verify CompileOnSaveAffectedFileList
const actualAffectedFiles = session.executeCommandSeq<protocol.CompileOnSaveAffectedFileListRequest>({
command: protocol.CommandTypes.CompileOnSaveAffectedFileList,
arguments: args
}).response as protocol.CompileOnSaveAffectedFileListSingleProject[];
assert.deepEqual(actualAffectedFiles, expectedAffected, "Affected files");
// Verify CompileOnSaveEmit
const actualEmit = session.executeCommandSeq<protocol.CompileOnSaveEmitFileRequest>({
command: protocol.CommandTypes.CompileOnSaveEmitFile,
arguments: args
}).response;
assert.deepEqual(actualEmit, expectedEmitSuccess, "Emit files");
assert.equal(host.writtenFiles.size, expectedFiles.length);
for (const file of expectedFiles) {
assert.equal(host.readFile(file.path), file.content, `Expected to write ${file.path}`);
assert.isTrue(host.writtenFiles.has(file.path), `${file.path} is newly written`);
}
// Verify EmitOutput
const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq<protocol.EmitOutputRequest>({
command: protocol.CommandTypes.EmitOutput,
arguments: args
}).response as EmitOutput;
assert.deepEqual(actualEmitOutput, expectedEmitOutput, "Emit output");
});
}
interface VerifySingleScenario {
scenario: string;
openFiles: () => readonly File[];
requestArgs: () => protocol.FileRequestArgs;
skipWithoutProject?: boolean;
change?: () => SingleScenarioChange;
expectedResult: GetSingleScenarioResult;
}
function verifySingleScenario(scenario: VerifySingleScenario) {
if (!scenario.skipWithoutProject) {
describe("without specifying project file", () => {
verifySingleScenarioWorker({
withProject: false,
...scenario
});
});
}
describe("with specifying project file", () => {
verifySingleScenarioWorker({
withProject: true,
...scenario
});
});
}
interface SingleScenarioExpectedEmit {
expectedEmitSuccess: boolean;
expectedFiles: readonly File[];
}
interface SingleScenarioResult {
expectedAffected: protocol.CompileOnSaveAffectedFileListSingleProject[];
expectedEmit: SingleScenarioExpectedEmit;
expectedEmitOutput: EmitOutput;
}
type GetSingleScenarioResult = (withProject: boolean) => SingleScenarioResult;
interface SingleScenarioChange {
file: File;
insertString: string;
}
interface ScenarioDetails {
scenarioName: string;
requestArgs: () => protocol.FileRequestArgs;
skipWithoutProject?: boolean;
initial: GetSingleScenarioResult;
localChangeToDependency: GetSingleScenarioResult;
localChangeToUsage: GetSingleScenarioResult;
changeToDependency: GetSingleScenarioResult;
changeToUsage: GetSingleScenarioResult;
}
interface VerifyScenario {
openFiles: () => readonly File[];
scenarios: readonly ScenarioDetails[];
}
const localChange = "function fn3() { }";
const change = `export ${localChange}`;
const changeJs = `function fn3() { }
exports.fn3 = fn3;`;
const changeDts = "export declare function fn3(): void;";
function verifyScenario({ openFiles, scenarios }: VerifyScenario) {
for (const {
scenarioName, requestArgs, skipWithoutProject, initial,
localChangeToDependency, localChangeToUsage,
changeToDependency, changeToUsage
} of scenarios) {
describe(scenarioName, () => {
verifySingleScenario({
scenario: "with initial file open",
openFiles,
requestArgs,
skipWithoutProject,
expectedResult: initial
});
verifySingleScenario({
scenario: "with local change to dependency",
openFiles,
requestArgs,
skipWithoutProject,
change: () => ({ file: dependencyTs, insertString: localChange }),
expectedResult: localChangeToDependency
});
verifySingleScenario({
scenario: "with local change to usage",
openFiles,
requestArgs,
skipWithoutProject,
change: () => ({ file: usageTs, insertString: localChange }),
expectedResult: localChangeToUsage
});
verifySingleScenario({
scenario: "with change to dependency",
openFiles,
requestArgs,
skipWithoutProject,
change: () => ({ file: dependencyTs, insertString: change }),
expectedResult: changeToDependency
});
verifySingleScenario({
scenario: "with change to usage",
openFiles,
requestArgs,
skipWithoutProject,
change: () => ({ file: usageTs, insertString: change }),
expectedResult: changeToUsage
});
});
}
}
function expectedAffectedFiles(config: File, fileNames: File[]): protocol.CompileOnSaveAffectedFileListSingleProject {
return {
projectFileName: config.path,
fileNames: fileNames.map(f => f.path),
projectUsesOutFile: false
};
}
function expectedUsageEmit(appendJsText?: string): SingleScenarioExpectedEmit {
const appendJs = appendJsText ? `${appendJsText}
` : "";
return {
expectedEmitSuccess: true,
expectedFiles: [{
path: `${usageLocation}/usage.js`,
content: `"use strict";
exports.__esModule = true;
var fns_1 = require("../decls/fns");
fns_1.fn1();
fns_1.fn2();
${appendJs}`
}]
};
}
function expectedEmitOutput({ expectedFiles }: SingleScenarioExpectedEmit): EmitOutput {
return {
outputFiles: expectedFiles.map(({ path, content }) => ({
name: path,
text: content,
writeByteOrderMark: false
})),
emitSkipped: false
};
}
function expectedUsageEmitOutput(appendJsText?: string): EmitOutput {
return expectedEmitOutput(expectedUsageEmit(appendJsText));
}
function noEmit(): SingleScenarioExpectedEmit {
return {
expectedEmitSuccess: false,
expectedFiles: emptyArray
};
}
function noEmitOutput(): EmitOutput {
return {
emitSkipped: true,
outputFiles: []
};
}
function expectedDependencyEmit(appendJsText?: string, appendDtsText?: string): SingleScenarioExpectedEmit {
const appendJs = appendJsText ? `${appendJsText}
` : "";
const appendDts = appendDtsText ? `${appendDtsText}
` : "";
return {
expectedEmitSuccess: true,
expectedFiles: [
{
path: `${dependecyLocation}/fns.js`,
content: `"use strict";
exports.__esModule = true;
function fn1() { }
exports.fn1 = fn1;
function fn2() { }
exports.fn2 = fn2;
${appendJs}`
},
{
path: `${projectLocation}/decls/fns.d.ts`,
content: `export declare function fn1(): void;
export declare function fn2(): void;
${appendDts}`
}
]
};
}
function expectedDependencyEmitOutput(appendJsText?: string, appendDtsText?: string): EmitOutput {
return expectedEmitOutput(expectedDependencyEmit(appendJsText, appendDtsText));
}
function scenarioDetailsOfUsage(isDependencyOpen?: boolean): ScenarioDetails[] {
return [
{
scenarioName: "Of usageTs",
requestArgs: () => ({ file: usageTs.path, projectFileName: usageConfig.path }),
initial: () => initialUsageTs(),
// no change to usage so same as initial only usage file
localChangeToDependency: () => initialUsageTs(),
localChangeToUsage: () => initialUsageTs(localChange),
changeToDependency: () => initialUsageTs(),
changeToUsage: () => initialUsageTs(changeJs)
},
{
scenarioName: "Of dependencyTs in usage project",
requestArgs: () => ({ file: dependencyTs.path, projectFileName: usageConfig.path }),
skipWithoutProject: !!isDependencyOpen,
initial: () => initialDependencyTs(),
localChangeToDependency: () => initialDependencyTs(/*noUsageFiles*/ true),
localChangeToUsage: () => initialDependencyTs(/*noUsageFiles*/ true),
changeToDependency: () => initialDependencyTs(),
changeToUsage: () => initialDependencyTs(/*noUsageFiles*/ true)
}
];
function initialUsageTs(jsText?: string) {
return {
expectedAffected: [
expectedAffectedFiles(usageConfig, [usageTs])
],
expectedEmit: expectedUsageEmit(jsText),
expectedEmitOutput: expectedUsageEmitOutput(jsText)
};
}
function initialDependencyTs(noUsageFiles?: true) {
return {
expectedAffected: [
expectedAffectedFiles(usageConfig, noUsageFiles ? [] : [usageTs])
],
expectedEmit: noEmit(),
expectedEmitOutput: noEmitOutput()
};
}
}
function scenarioDetailsOfDependencyWhenOpen(): ScenarioDetails {
return {
scenarioName: "Of dependencyTs",
requestArgs: () => ({ file: dependencyTs.path, projectFileName: dependencyConfig.path }),
initial,
localChangeToDependency: withProject => ({
expectedAffected: withProject ?
[
expectedAffectedFiles(dependencyConfig, [dependencyTs])
] :
[
expectedAffectedFiles(usageConfig, []),
expectedAffectedFiles(dependencyConfig, [dependencyTs])
],
expectedEmit: expectedDependencyEmit(localChange),
expectedEmitOutput: expectedDependencyEmitOutput(localChange)
}),
localChangeToUsage: withProject => initial(withProject, /*noUsageFiles*/ true),
changeToDependency: withProject => initial(withProject, /*noUsageFiles*/ undefined, changeJs, changeDts),
changeToUsage: withProject => initial(withProject, /*noUsageFiles*/ true)
};
function initial(withProject: boolean, noUsageFiles?: true, appendJs?: string, appendDts?: string): SingleScenarioResult {
return {
expectedAffected: withProject ?
[
expectedAffectedFiles(dependencyConfig, [dependencyTs])
] :
[
expectedAffectedFiles(usageConfig, noUsageFiles ? [] : [usageTs]),
expectedAffectedFiles(dependencyConfig, [dependencyTs])
],
expectedEmit: expectedDependencyEmit(appendJs, appendDts),
expectedEmitOutput: expectedDependencyEmitOutput(appendJs, appendDts)
};
}
}
describe("when dependency project is not open", () => {
verifyScenario({
openFiles: () => [usageTs],
scenarios: scenarioDetailsOfUsage()
});
});
describe("when the depedency file is open", () => {
verifyScenario({
openFiles: () => [usageTs, dependencyTs],
scenarios: [
...scenarioDetailsOfUsage(/*isDependencyOpen*/ true),
scenarioDetailsOfDependencyWhenOpen(),
]
});
});
});
}
@@ -0,0 +1,430 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: with project references and error reporting", () => {
const projectLocation = "/user/username/projects/myproject";
const dependecyLocation = `${projectLocation}/dependency`;
const usageLocation = `${projectLocation}/usage`;
interface CheckErrorsInFile {
session: TestSession;
host: TestServerHost;
expected: GetErrDiagnostics;
expectedSequenceId?: number;
}
function checkErrorsInFile({ session, host, expected: { file, syntax, semantic, suggestion }, expectedSequenceId }: CheckErrorsInFile) {
host.checkTimeoutQueueLengthAndRun(1);
checkErrorMessage(session, "syntaxDiag", { file: file.path, diagnostics: syntax });
session.clearMessages();
host.runQueuedImmediateCallbacks(1);
checkErrorMessage(session, "semanticDiag", { file: file.path, diagnostics: semantic });
session.clearMessages();
host.runQueuedImmediateCallbacks(1);
checkErrorMessage(session, "suggestionDiag", { file: file.path, diagnostics: suggestion });
if (expectedSequenceId !== undefined) {
checkCompleteEvent(session, 2, expectedSequenceId);
}
session.clearMessages();
}
interface CheckAllErrors {
session: TestSession;
host: TestServerHost;
expected: readonly GetErrDiagnostics[];
expectedSequenceId: number;
}
function checkAllErrors({ session, host, expected, expectedSequenceId }: CheckAllErrors) {
for (let i = 0; i < expected.length; i++) {
checkErrorsInFile({
session,
host,
expected: expected[i],
expectedSequenceId: i === expected.length - 1 ? expectedSequenceId : undefined
});
}
}
function verifyErrorsUsingGeterr({ allFiles, openFiles, expectedGetErr }: VerifyScenario) {
it("verifies the errors in open file", () => {
const host = createServerHost([...allFiles(), libFile]);
const session = createSession(host, { canUseEvents: true, });
openFilesForSession(openFiles(), session);
session.clearMessages();
const expectedSequenceId = session.getNextSeq();
const expected = expectedGetErr();
session.executeCommandSeq<protocol.GeterrRequest>({
command: protocol.CommandTypes.Geterr,
arguments: {
delay: 0,
files: expected.map(f => f.file.path)
}
});
checkAllErrors({ session, host, expected, expectedSequenceId });
});
}
function verifyErrorsUsingGeterrForProject({ allFiles, openFiles, expectedGetErrForProject }: VerifyScenario) {
it("verifies the errors in projects", () => {
const host = createServerHost([...allFiles(), libFile]);
const session = createSession(host, { canUseEvents: true, });
openFilesForSession(openFiles(), session);
session.clearMessages();
for (const expected of expectedGetErrForProject()) {
const expectedSequenceId = session.getNextSeq();
session.executeCommandSeq<protocol.GeterrForProjectRequest>({
command: protocol.CommandTypes.GeterrForProject,
arguments: {
delay: 0,
file: expected.project
}
});
checkAllErrors({ session, host, expected: expected.errors, expectedSequenceId });
}
});
}
function verifyErrorsUsingSyncMethods({ allFiles, openFiles, expectedSyncDiagnostics }: VerifyScenario) {
it("verifies the errors using sync commands", () => {
const host = createServerHost([...allFiles(), libFile]);
const session = createSession(host);
openFilesForSession(openFiles(), session);
for (const { file, project, syntax, semantic, suggestion } of expectedSyncDiagnostics()) {
const actualSyntax = session.executeCommandSeq<protocol.SyntacticDiagnosticsSyncRequest>({
command: protocol.CommandTypes.SyntacticDiagnosticsSync,
arguments: {
file: file.path,
projectFileName: project
}
}).response as protocol.Diagnostic[];
assert.deepEqual(actualSyntax, syntax, `Syntax diagnostics for file: ${file.path}, project: ${project}`);
const actualSemantic = session.executeCommandSeq<protocol.SemanticDiagnosticsSyncRequest>({
command: protocol.CommandTypes.SemanticDiagnosticsSync,
arguments: {
file: file.path,
projectFileName: project
}
}).response as protocol.Diagnostic[];
assert.deepEqual(actualSemantic, semantic, `Semantic diagnostics for file: ${file.path}, project: ${project}`);
const actualSuggestion = session.executeCommandSeq<protocol.SuggestionDiagnosticsSyncRequest>({
command: protocol.CommandTypes.SuggestionDiagnosticsSync,
arguments: {
file: file.path,
projectFileName: project
}
}).response as protocol.Diagnostic[];
assert.deepEqual(actualSuggestion, suggestion, `Suggestion diagnostics for file: ${file.path}, project: ${project}`);
}
});
}
function verifyConfigFileErrors({ allFiles, openFiles, expectedConfigFileDiagEvents }: VerifyScenario) {
it("verify config file errors", () => {
const host = createServerHost([...allFiles(), libFile]);
const { session, events } = createSessionWithEventTracking<server.ConfigFileDiagEvent>(host, server.ConfigFileDiagEvent);
for (const file of openFiles()) {
session.executeCommandSeq<protocol.OpenRequest>({
command: protocol.CommandTypes.Open,
arguments: { file: file.path }
});
}
assert.deepEqual(events, expectedConfigFileDiagEvents().map(data => ({
eventName: server.ConfigFileDiagEvent,
data
})));
});
}
interface GetErrDiagnostics {
file: File;
syntax: protocol.Diagnostic[];
semantic: protocol.Diagnostic[];
suggestion: protocol.Diagnostic[];
}
interface GetErrForProjectDiagnostics {
project: string;
errors: readonly GetErrDiagnostics[];
}
interface SyncDiagnostics extends GetErrDiagnostics {
project?: string;
}
interface VerifyScenario {
allFiles: () => readonly File[];
openFiles: () => readonly File[];
expectedGetErr: () => readonly GetErrDiagnostics[];
expectedGetErrForProject: () => readonly GetErrForProjectDiagnostics[];
expectedSyncDiagnostics: () => readonly SyncDiagnostics[];
expectedConfigFileDiagEvents: () => readonly server.ConfigFileDiagEvent["data"][];
}
function verifyScenario(scenario: VerifyScenario) {
verifyErrorsUsingGeterr(scenario);
verifyErrorsUsingGeterrForProject(scenario);
verifyErrorsUsingSyncMethods(scenario);
verifyConfigFileErrors(scenario);
}
function emptyDiagnostics(file: File): GetErrDiagnostics {
return {
file,
syntax: emptyArray,
semantic: emptyArray,
suggestion: emptyArray
};
}
function syncDiagnostics(diagnostics: GetErrDiagnostics, project: string): SyncDiagnostics {
return { project, ...diagnostics };
}
interface VerifyUsageAndDependency {
allFiles: readonly [File, File, File, File]; // dependencyTs, dependencyConfig, usageTs, usageConfig
usageDiagnostics(): GetErrDiagnostics;
dependencyDiagnostics(): GetErrDiagnostics;
}
function verifyUsageAndDependency({ allFiles, usageDiagnostics, dependencyDiagnostics }: VerifyUsageAndDependency) {
const [dependencyTs, dependencyConfig, usageTs, usageConfig] = allFiles;
function usageProjectDiagnostics(): GetErrForProjectDiagnostics {
return {
project: usageTs.path,
errors: [
usageDiagnostics(),
emptyDiagnostics(dependencyTs)
]
};
}
function dependencyProjectDiagnostics(): GetErrForProjectDiagnostics {
return {
project: dependencyTs.path,
errors: [
dependencyDiagnostics()
]
};
}
function usageConfigDiag(): server.ConfigFileDiagEvent["data"] {
return {
triggerFile: usageTs.path,
configFileName: usageConfig.path,
diagnostics: emptyArray
};
}
function dependencyConfigDiag(): server.ConfigFileDiagEvent["data"] {
return {
triggerFile: dependencyTs.path,
configFileName: dependencyConfig.path,
diagnostics: emptyArray
};
}
describe("when dependency project is not open", () => {
verifyScenario({
allFiles: () => allFiles,
openFiles: () => [usageTs],
expectedGetErr: () => [
usageDiagnostics()
],
expectedGetErrForProject: () => [
usageProjectDiagnostics(),
{
project: dependencyTs.path,
errors: [
emptyDiagnostics(dependencyTs),
usageDiagnostics()
]
}
],
expectedSyncDiagnostics: () => [
// Without project
usageDiagnostics(),
emptyDiagnostics(dependencyTs),
// With project
syncDiagnostics(usageDiagnostics(), usageConfig.path),
syncDiagnostics(emptyDiagnostics(dependencyTs), usageConfig.path),
],
expectedConfigFileDiagEvents: () => [
usageConfigDiag()
],
});
});
describe("when the depedency file is open", () => {
verifyScenario({
allFiles: () => allFiles,
openFiles: () => [usageTs, dependencyTs],
expectedGetErr: () => [
usageDiagnostics(),
dependencyDiagnostics(),
],
expectedGetErrForProject: () => [
usageProjectDiagnostics(),
dependencyProjectDiagnostics()
],
expectedSyncDiagnostics: () => [
// Without project
usageDiagnostics(),
dependencyDiagnostics(),
// With project
syncDiagnostics(usageDiagnostics(), usageConfig.path),
syncDiagnostics(emptyDiagnostics(dependencyTs), usageConfig.path),
syncDiagnostics(dependencyDiagnostics(), dependencyConfig.path),
],
expectedConfigFileDiagEvents: () => [
usageConfigDiag(),
dependencyConfigDiag()
],
});
});
}
describe("with module scenario", () => {
const dependencyTs: File = {
path: `${dependecyLocation}/fns.ts`,
content: `export function fn1() { }
export function fn2() { }
// Introduce error for fnErr import in main
// export function fnErr() { }
// Error in dependency ts file
export let x: string = 10;`
};
const dependencyConfig: File = {
path: `${dependecyLocation}/tsconfig.json`,
content: JSON.stringify({ compilerOptions: { composite: true, declarationDir: "../decls" } })
};
const usageTs: File = {
path: `${usageLocation}/usage.ts`,
content: `import {
fn1,
fn2,
fnErr
} from '../decls/fns'
fn1();
fn2();
fnErr();
`
};
const usageConfig: File = {
path: `${usageLocation}/tsconfig.json`,
content: JSON.stringify({
compilerOptions: { composite: true },
references: [{ path: "../dependency" }]
})
};
function usageDiagnostics(): GetErrDiagnostics {
return {
file: usageTs,
syntax: emptyArray,
semantic: [
createDiagnostic(
{ line: 4, offset: 5 },
{ line: 4, offset: 10 },
Diagnostics.Module_0_has_no_exported_member_1,
[`"../dependency/fns"`, "fnErr"],
"error",
)
],
suggestion: emptyArray
};
}
function dependencyDiagnostics(): GetErrDiagnostics {
return {
file: dependencyTs,
syntax: emptyArray,
semantic: [
createDiagnostic(
{ line: 6, offset: 12 },
{ line: 6, offset: 13 },
Diagnostics.Type_0_is_not_assignable_to_type_1,
["10", "string"],
"error",
)
],
suggestion: emptyArray
};
}
verifyUsageAndDependency({
allFiles: [dependencyTs, dependencyConfig, usageTs, usageConfig],
usageDiagnostics,
dependencyDiagnostics
});
});
describe("with non module --out", () => {
const dependencyTs: File = {
path: `${dependecyLocation}/fns.ts`,
content: `function fn1() { }
function fn2() { }
// Introduce error for fnErr import in main
// function fnErr() { }
// Error in dependency ts file
let x: string = 10;`
};
const dependencyConfig: File = {
path: `${dependecyLocation}/tsconfig.json`,
content: JSON.stringify({ compilerOptions: { composite: true, outFile: "../dependency.js" } })
};
const usageTs: File = {
path: `${usageLocation}/usage.ts`,
content: `fn1();
fn2();
fnErr();
`
};
const usageConfig: File = {
path: `${usageLocation}/tsconfig.json`,
content: JSON.stringify({
compilerOptions: { composite: true, outFile: "../usage.js" },
references: [{ path: "../dependency" }]
})
};
function usageDiagnostics(): GetErrDiagnostics {
return {
file: usageTs,
syntax: emptyArray,
semantic: [
createDiagnostic(
{ line: 3, offset: 1 },
{ line: 3, offset: 6 },
Diagnostics.Cannot_find_name_0,
["fnErr"],
"error",
)
],
suggestion: emptyArray
};
}
function dependencyDiagnostics(): GetErrDiagnostics {
return {
file: dependencyTs,
syntax: emptyArray,
semantic: [
createDiagnostic(
{ line: 6, offset: 5 },
{ line: 6, offset: 6 },
Diagnostics.Type_0_is_not_assignable_to_type_1,
["10", "string"],
"error",
)
],
suggestion: emptyArray
};
}
verifyUsageAndDependency({
allFiles: [dependencyTs, dependencyConfig, usageTs, usageConfig],
usageDiagnostics,
dependencyDiagnostics
});
});
});
}
File diff suppressed because it is too large Load Diff