Merge pull request #32028 from microsoft/referencesPrototypeSourceFile
For editing experience, use source instead of .d.ts files from project references
This commit is contained in:
commit
992c211c22
|
@ -425,7 +425,7 @@ namespace ts {
|
||||||
const options = program.getCompilerOptions();
|
const options = program.getCompilerOptions();
|
||||||
forEach(program.getSourceFiles(), f =>
|
forEach(program.getSourceFiles(), f =>
|
||||||
program.isSourceFileDefaultLibrary(f) &&
|
program.isSourceFileDefaultLibrary(f) &&
|
||||||
!skipTypeChecking(f, options) &&
|
!skipTypeChecking(f, options, program) &&
|
||||||
removeSemanticDiagnosticsOf(state, f.path)
|
removeSemanticDiagnosticsOf(state, f.path)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,7 +572,7 @@ namespace ts {
|
||||||
return node && getTypeArgumentConstraint(node);
|
return node && getTypeArgumentConstraint(node);
|
||||||
},
|
},
|
||||||
getSuggestionDiagnostics: (file, ct) => {
|
getSuggestionDiagnostics: (file, ct) => {
|
||||||
if (skipTypeChecking(file, compilerOptions)) {
|
if (skipTypeChecking(file, compilerOptions, host)) {
|
||||||
return emptyArray;
|
return emptyArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31048,7 +31048,7 @@ namespace ts {
|
||||||
function checkSourceFileWorker(node: SourceFile) {
|
function checkSourceFileWorker(node: SourceFile) {
|
||||||
const links = getNodeLinks(node);
|
const links = getNodeLinks(node);
|
||||||
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
|
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
|
||||||
if (skipTypeChecking(node, compilerOptions)) {
|
if (skipTypeChecking(node, compilerOptions, host)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -772,6 +772,12 @@ namespace ts {
|
||||||
category: Diagnostics.Advanced_Options,
|
category: Diagnostics.Advanced_Options,
|
||||||
description: Diagnostics.Disable_size_limitations_on_JavaScript_projects
|
description: Diagnostics.Disable_size_limitations_on_JavaScript_projects
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "disableSourceOfProjectReferenceRedirect",
|
||||||
|
type: "boolean",
|
||||||
|
category: Diagnostics.Advanced_Options,
|
||||||
|
description: Diagnostics.Disable_use_of_source_files_instead_of_declaration_files_from_referenced_projects
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "noImplicitUseStrict",
|
name: "noImplicitUseStrict",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
|
|
|
@ -4036,6 +4036,10 @@
|
||||||
"category": "Message",
|
"category": "Message",
|
||||||
"code": 6220
|
"code": 6220
|
||||||
},
|
},
|
||||||
|
"Disable use of source files instead of declaration files from referenced projects.": {
|
||||||
|
"category": "Message",
|
||||||
|
"code": 6221
|
||||||
|
},
|
||||||
|
|
||||||
"Projects to reference": {
|
"Projects to reference": {
|
||||||
"category": "Message",
|
"category": "Message",
|
||||||
|
|
|
@ -817,6 +817,8 @@ namespace ts {
|
||||||
let resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined;
|
let resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined;
|
||||||
let projectReferenceRedirects: Map<ResolvedProjectReference | false> | undefined;
|
let projectReferenceRedirects: Map<ResolvedProjectReference | false> | undefined;
|
||||||
let mapFromFileToProjectReferenceRedirects: Map<Path> | undefined;
|
let mapFromFileToProjectReferenceRedirects: Map<Path> | undefined;
|
||||||
|
let mapFromToProjectReferenceRedirectSource: Map<SourceOfProjectReferenceRedirect> | undefined;
|
||||||
|
const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect && host.useSourceOfProjectReferenceRedirect();
|
||||||
|
|
||||||
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
|
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
|
||||||
// We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
|
// We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
|
||||||
|
@ -831,17 +833,32 @@ namespace ts {
|
||||||
if (!resolvedProjectReferences) {
|
if (!resolvedProjectReferences) {
|
||||||
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
|
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
|
||||||
}
|
}
|
||||||
|
if (host.setResolvedProjectReferenceCallbacks) {
|
||||||
|
host.setResolvedProjectReferenceCallbacks({
|
||||||
|
getSourceOfProjectReferenceRedirect,
|
||||||
|
forEachResolvedProjectReference
|
||||||
|
});
|
||||||
|
}
|
||||||
if (rootNames.length) {
|
if (rootNames.length) {
|
||||||
for (const parsedRef of resolvedProjectReferences) {
|
for (const parsedRef of resolvedProjectReferences) {
|
||||||
if (!parsedRef) continue;
|
if (!parsedRef) continue;
|
||||||
const out = parsedRef.commandLine.options.outFile || parsedRef.commandLine.options.out;
|
const out = parsedRef.commandLine.options.outFile || parsedRef.commandLine.options.out;
|
||||||
if (out) {
|
if (useSourceOfProjectReferenceRedirect) {
|
||||||
processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
|
||||||
|
for (const fileName of parsedRef.commandLine.fileNames) {
|
||||||
|
processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
|
else {
|
||||||
for (const fileName of parsedRef.commandLine.fileNames) {
|
if (out) {
|
||||||
if (!fileExtensionIs(fileName, Extension.Dts) && hasTSFileExtension(fileName)) {
|
processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
||||||
processSourceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames()), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
}
|
||||||
|
else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
|
||||||
|
for (const fileName of parsedRef.commandLine.fileNames) {
|
||||||
|
if (!fileExtensionIs(fileName, Extension.Dts) && hasTSFileExtension(fileName)) {
|
||||||
|
processSourceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames()), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -955,6 +972,7 @@ namespace ts {
|
||||||
getResolvedProjectReferenceToRedirect,
|
getResolvedProjectReferenceToRedirect,
|
||||||
getResolvedProjectReferenceByPath,
|
getResolvedProjectReferenceByPath,
|
||||||
forEachResolvedProjectReference,
|
forEachResolvedProjectReference,
|
||||||
|
isSourceOfProjectReferenceRedirect,
|
||||||
emitBuildInfo
|
emitBuildInfo
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -987,9 +1005,15 @@ namespace ts {
|
||||||
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidSourceFileForEmit(file: SourceFile) {
|
||||||
|
// source file is allowed to be emitted and its not source of project reference redirect
|
||||||
|
return sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect) &&
|
||||||
|
!isSourceOfProjectReferenceRedirect(file.fileName);
|
||||||
|
}
|
||||||
|
|
||||||
function getCommonSourceDirectory() {
|
function getCommonSourceDirectory() {
|
||||||
if (commonSourceDirectory === undefined) {
|
if (commonSourceDirectory === undefined) {
|
||||||
const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect));
|
const emittedFiles = filter(files, file => isValidSourceFileForEmit(file));
|
||||||
if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) {
|
if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) {
|
||||||
// If a rootDir is specified use it as the commonSourceDirectory
|
// If a rootDir is specified use it as the commonSourceDirectory
|
||||||
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
|
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
|
||||||
|
@ -1220,6 +1244,12 @@ namespace ts {
|
||||||
}
|
}
|
||||||
if (projectReferences) {
|
if (projectReferences) {
|
||||||
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
|
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
|
||||||
|
if (host.setResolvedProjectReferenceCallbacks) {
|
||||||
|
host.setResolvedProjectReferenceCallbacks({
|
||||||
|
getSourceOfProjectReferenceRedirect,
|
||||||
|
forEachResolvedProjectReference
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if program source files has changed in the way that can affect structure of the program
|
// check if program source files has changed in the way that can affect structure of the program
|
||||||
|
@ -1403,6 +1433,13 @@ namespace ts {
|
||||||
for (const newSourceFile of newSourceFiles) {
|
for (const newSourceFile of newSourceFiles) {
|
||||||
const filePath = newSourceFile.path;
|
const filePath = newSourceFile.path;
|
||||||
addFileToFilesByName(newSourceFile, filePath, newSourceFile.resolvedPath);
|
addFileToFilesByName(newSourceFile, filePath, newSourceFile.resolvedPath);
|
||||||
|
if (useSourceOfProjectReferenceRedirect) {
|
||||||
|
const redirectProject = getProjectReferenceRedirectProject(newSourceFile.fileName);
|
||||||
|
if (redirectProject && !(redirectProject.commandLine.options.outFile || redirectProject.commandLine.options.out)) {
|
||||||
|
const redirect = getProjectReferenceOutputName(redirectProject, newSourceFile.fileName);
|
||||||
|
addFileToFilesByName(newSourceFile, toPath(redirect), /*redirectedPath*/ undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Set the file as found during node modules search if it was found that way in old progra,
|
// Set the file as found during node modules search if it was found that way in old progra,
|
||||||
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(newSourceFile.resolvedPath)!)) {
|
if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(newSourceFile.resolvedPath)!)) {
|
||||||
sourceFilesFoundSearchingNodeModules.set(filePath, true);
|
sourceFilesFoundSearchingNodeModules.set(filePath, true);
|
||||||
|
@ -1682,7 +1719,7 @@ namespace ts {
|
||||||
|
|
||||||
function getSemanticDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] | undefined {
|
function getSemanticDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] | undefined {
|
||||||
return runWithCancellationToken(() => {
|
return runWithCancellationToken(() => {
|
||||||
if (skipTypeChecking(sourceFile, options)) {
|
if (skipTypeChecking(sourceFile, options, program)) {
|
||||||
return emptyArray;
|
return emptyArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2234,6 +2271,16 @@ namespace ts {
|
||||||
|
|
||||||
// Get source file from normalized fileName
|
// Get source file from normalized fileName
|
||||||
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile: RefFile | undefined, packageId: PackageId | undefined): SourceFile | undefined {
|
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile: RefFile | undefined, packageId: PackageId | undefined): SourceFile | undefined {
|
||||||
|
if (useSourceOfProjectReferenceRedirect) {
|
||||||
|
const source = getSourceOfProjectReferenceRedirect(fileName);
|
||||||
|
if (source) {
|
||||||
|
const file = isString(source) ?
|
||||||
|
findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, refFile, packageId) :
|
||||||
|
undefined;
|
||||||
|
if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
const originalFileName = fileName;
|
const originalFileName = fileName;
|
||||||
if (filesByName.has(path)) {
|
if (filesByName.has(path)) {
|
||||||
const file = filesByName.get(path);
|
const file = filesByName.get(path);
|
||||||
|
@ -2282,7 +2329,7 @@ namespace ts {
|
||||||
}
|
}
|
||||||
|
|
||||||
let redirectedPath: Path | undefined;
|
let redirectedPath: Path | undefined;
|
||||||
if (refFile) {
|
if (refFile && !useSourceOfProjectReferenceRedirect) {
|
||||||
const redirectProject = getProjectReferenceRedirectProject(fileName);
|
const redirectProject = getProjectReferenceRedirectProject(fileName);
|
||||||
if (redirectProject) {
|
if (redirectProject) {
|
||||||
if (redirectProject.commandLine.options.outFile || redirectProject.commandLine.options.out) {
|
if (redirectProject.commandLine.options.outFile || redirectProject.commandLine.options.out) {
|
||||||
|
@ -2451,6 +2498,36 @@ namespace ts {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSourceOfProjectReferenceRedirect(file: string) {
|
||||||
|
if (!isDeclarationFileName(file)) return undefined;
|
||||||
|
if (mapFromToProjectReferenceRedirectSource === undefined) {
|
||||||
|
mapFromToProjectReferenceRedirectSource = createMap();
|
||||||
|
forEachResolvedProjectReference(resolvedRef => {
|
||||||
|
if (resolvedRef) {
|
||||||
|
const out = resolvedRef.commandLine.options.outFile || resolvedRef.commandLine.options.out;
|
||||||
|
if (out) {
|
||||||
|
// Dont know which source file it means so return true?
|
||||||
|
const outputDts = changeExtension(out, Extension.Dts);
|
||||||
|
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
forEach(resolvedRef.commandLine.fileNames, fileName => {
|
||||||
|
if (!fileExtensionIs(fileName, Extension.Dts) && hasTSFileExtension(fileName)) {
|
||||||
|
const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, host.useCaseSensitiveFileNames());
|
||||||
|
mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return mapFromToProjectReferenceRedirectSource.get(toPath(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSourceOfProjectReferenceRedirect(fileName: string) {
|
||||||
|
return useSourceOfProjectReferenceRedirect && !!getResolvedProjectReferenceToRedirect(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
function forEachProjectReference<T>(
|
function forEachProjectReference<T>(
|
||||||
projectReferences: readonly ProjectReference[] | undefined,
|
projectReferences: readonly ProjectReference[] | undefined,
|
||||||
resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
|
resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
|
||||||
|
@ -2858,8 +2935,7 @@ namespace ts {
|
||||||
const rootPaths = arrayToSet(rootNames, toPath);
|
const rootPaths = arrayToSet(rootNames, toPath);
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
// Ignore file that is not emitted
|
// Ignore file that is not emitted
|
||||||
if (!sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect)) continue;
|
if (isValidSourceFileForEmit(file) && !rootPaths.has(file.path)) {
|
||||||
if (!rootPaths.has(file.path)) {
|
|
||||||
addProgramDiagnosticAtRefPath(
|
addProgramDiagnosticAtRefPath(
|
||||||
file,
|
file,
|
||||||
rootPaths,
|
rootPaths,
|
||||||
|
|
|
@ -522,6 +522,33 @@ namespace ts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recursiveCreateDirectory(directoryPath: string, sys: System) {
|
||||||
|
const basePath = getDirectoryPath(directoryPath);
|
||||||
|
const shouldCreateParent = basePath !== "" && directoryPath !== basePath && !sys.directoryExists(basePath);
|
||||||
|
if (shouldCreateParent) {
|
||||||
|
recursiveCreateDirectory(basePath, sys);
|
||||||
|
}
|
||||||
|
if (shouldCreateParent || !sys.directoryExists(directoryPath)) {
|
||||||
|
sys.createDirectory(directoryPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* patch writefile to create folder before writing the file
|
||||||
|
*/
|
||||||
|
/*@internal*/
|
||||||
|
export function patchWriteFileEnsuringDirectory(sys: System) {
|
||||||
|
// patch writefile to create folder before writing the file
|
||||||
|
const originalWriteFile = sys.writeFile;
|
||||||
|
sys.writeFile = (path, data, writeBom) => {
|
||||||
|
const directoryPath = getDirectoryPath(normalizeSlashes(path));
|
||||||
|
if (directoryPath && !sys.directoryExists(directoryPath)) {
|
||||||
|
recursiveCreateDirectory(directoryPath, sys);
|
||||||
|
}
|
||||||
|
originalWriteFile.call(sys, path, data, writeBom);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/*@internal*/
|
/*@internal*/
|
||||||
export type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex";
|
export type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex";
|
||||||
|
|
||||||
|
@ -1365,17 +1392,6 @@ namespace ts {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function recursiveCreateDirectory(directoryPath: string, sys: System) {
|
|
||||||
const basePath = getDirectoryPath(directoryPath);
|
|
||||||
const shouldCreateParent = basePath !== "" && directoryPath !== basePath && !sys.directoryExists(basePath);
|
|
||||||
if (shouldCreateParent) {
|
|
||||||
recursiveCreateDirectory(basePath, sys);
|
|
||||||
}
|
|
||||||
if (shouldCreateParent || !sys.directoryExists(directoryPath)) {
|
|
||||||
sys.createDirectory(directoryPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let sys: System | undefined;
|
let sys: System | undefined;
|
||||||
if (typeof ChakraHost !== "undefined") {
|
if (typeof ChakraHost !== "undefined") {
|
||||||
sys = getChakraSystem();
|
sys = getChakraSystem();
|
||||||
|
@ -1387,14 +1403,7 @@ namespace ts {
|
||||||
}
|
}
|
||||||
if (sys) {
|
if (sys) {
|
||||||
// patch writefile to create folder before writing the file
|
// patch writefile to create folder before writing the file
|
||||||
const originalWriteFile = sys.writeFile;
|
patchWriteFileEnsuringDirectory(sys);
|
||||||
sys.writeFile = (path, data, writeBom) => {
|
|
||||||
const directoryPath = getDirectoryPath(normalizeSlashes(path));
|
|
||||||
if (directoryPath && !sys!.directoryExists(directoryPath)) {
|
|
||||||
recursiveCreateDirectory(directoryPath, sys!);
|
|
||||||
}
|
|
||||||
originalWriteFile.call(sys, path, data, writeBom);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return sys!;
|
return sys!;
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -3065,6 +3065,7 @@ namespace ts {
|
||||||
/*@internal*/ getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined;
|
/*@internal*/ getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined;
|
||||||
/*@internal*/ forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined;
|
/*@internal*/ forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined;
|
||||||
/*@internal*/ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined;
|
/*@internal*/ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined;
|
||||||
|
/*@internal*/ isSourceOfProjectReferenceRedirect(fileName: string): boolean;
|
||||||
/*@internal*/ getProgramBuildInfo?(): ProgramBuildInfo | undefined;
|
/*@internal*/ getProgramBuildInfo?(): ProgramBuildInfo | undefined;
|
||||||
/*@internal*/ emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
|
/*@internal*/ emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
|
||||||
}
|
}
|
||||||
|
@ -3165,6 +3166,7 @@ namespace ts {
|
||||||
getSourceFile(fileName: string): SourceFile | undefined;
|
getSourceFile(fileName: string): SourceFile | undefined;
|
||||||
getResolvedTypeReferenceDirectives(): ReadonlyMap<ResolvedTypeReferenceDirective | undefined>;
|
getResolvedTypeReferenceDirectives(): ReadonlyMap<ResolvedTypeReferenceDirective | undefined>;
|
||||||
getProjectReferenceRedirect(fileName: string): string | undefined;
|
getProjectReferenceRedirect(fileName: string): string | undefined;
|
||||||
|
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
|
||||||
|
|
||||||
readonly redirectTargetsMap: RedirectTargetsMap;
|
readonly redirectTargetsMap: RedirectTargetsMap;
|
||||||
}
|
}
|
||||||
|
@ -4775,6 +4777,7 @@ namespace ts {
|
||||||
/* @internal */ diagnostics?: boolean;
|
/* @internal */ diagnostics?: boolean;
|
||||||
/* @internal */ extendedDiagnostics?: boolean;
|
/* @internal */ extendedDiagnostics?: boolean;
|
||||||
disableSizeLimit?: boolean;
|
disableSizeLimit?: boolean;
|
||||||
|
disableSourceOfProjectReferenceRedirect?: boolean;
|
||||||
downlevelIteration?: boolean;
|
downlevelIteration?: boolean;
|
||||||
emitBOM?: boolean;
|
emitBOM?: boolean;
|
||||||
emitDecoratorMetadata?: boolean;
|
emitDecoratorMetadata?: boolean;
|
||||||
|
@ -5303,11 +5306,23 @@ namespace ts {
|
||||||
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
|
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
|
||||||
createHash?(data: string): string;
|
createHash?(data: string): string;
|
||||||
getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined;
|
getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined;
|
||||||
|
/* @internal */ setResolvedProjectReferenceCallbacks?(callbacks: ResolvedProjectReferenceCallbacks): void;
|
||||||
|
/* @internal */ useSourceOfProjectReferenceRedirect?(): boolean;
|
||||||
|
|
||||||
// TODO: later handle this in better way in builder host instead once the api for tsbuild finalizes and doesn't use compilerHost as base
|
// TODO: later handle this in better way in builder host instead once the api for tsbuild finalizes and doesn't use compilerHost as base
|
||||||
/*@internal*/createDirectory?(directory: string): void;
|
/*@internal*/createDirectory?(directory: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** true if --out otherwise source file name */
|
||||||
|
/*@internal*/
|
||||||
|
export type SourceOfProjectReferenceRedirect = string | true;
|
||||||
|
|
||||||
|
/*@internal*/
|
||||||
|
export interface ResolvedProjectReferenceCallbacks {
|
||||||
|
getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined;
|
||||||
|
forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference | undefined, resolvedProjectReferencePath: Path) => T | undefined): T | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
export const enum TransformFlags {
|
export const enum TransformFlags {
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|
|
@ -8713,11 +8713,16 @@ namespace ts {
|
||||||
return { pos: typeParameters.pos - 1, end: typeParameters.end + 1 };
|
return { pos: typeParameters.pos - 1, end: typeParameters.end + 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOptions) {
|
export interface HostWithIsSourceOfProjectReferenceRedirect {
|
||||||
|
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
|
||||||
|
}
|
||||||
|
export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOptions, host: HostWithIsSourceOfProjectReferenceRedirect) {
|
||||||
// If skipLibCheck is enabled, skip reporting errors if file is a declaration file.
|
// If skipLibCheck is enabled, skip reporting errors if file is a declaration file.
|
||||||
// If skipDefaultLibCheck is enabled, skip reporting errors if file contains a
|
// If skipDefaultLibCheck is enabled, skip reporting errors if file contains a
|
||||||
// '/// <reference no-default-lib="true"/>' directive.
|
// '/// <reference no-default-lib="true"/>' directive.
|
||||||
return options.skipLibCheck && sourceFile.isDeclarationFile || options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib;
|
return (options.skipLibCheck && sourceFile.isDeclarationFile ||
|
||||||
|
options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib) ||
|
||||||
|
host.isSourceOfProjectReferenceRedirect(sourceFile.fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isJsonEqual(a: unknown, b: unknown): boolean {
|
export function isJsonEqual(a: unknown, b: unknown): boolean {
|
||||||
|
|
|
@ -44,7 +44,10 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createServerHost(fileOrFolderList: readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters): TestServerHost {
|
export function createServerHost(fileOrFolderList: readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters): TestServerHost {
|
||||||
return new TestServerHost(/*withSafelist*/ true, fileOrFolderList, params);
|
const host = new TestServerHost(/*withSafelist*/ true, fileOrFolderList, params);
|
||||||
|
// Just like sys, patch the host to use writeFile
|
||||||
|
patchWriteFileEnsuringDirectory(host);
|
||||||
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface File {
|
export interface File {
|
||||||
|
@ -174,8 +177,8 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkWatchedFiles(host: TestServerHost, expectedFiles: string[]) {
|
export function checkWatchedFiles(host: TestServerHost, expectedFiles: string[], additionalInfo?: string) {
|
||||||
checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles);
|
checkMapKeys(`watchedFiles:: ${additionalInfo || ""}::`, host.watchedFiles, expectedFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkWatchedFilesDetailed(host: TestServerHost, expectedFiles: ReadonlyMap<number>): void;
|
export function checkWatchedFilesDetailed(host: TestServerHost, expectedFiles: ReadonlyMap<number>): void;
|
||||||
|
@ -1016,6 +1019,19 @@ interface Array<T> { length: number; [n: number]: T; }`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TestServerHostTrackingWrittenFiles = TestServerHost & { writtenFiles: Map<true>; };
|
||||||
|
|
||||||
|
export function changeToHostTrackingWrittenFiles(inputHost: TestServerHost) {
|
||||||
|
const host = inputHost as TestServerHostTrackingWrittenFiles;
|
||||||
|
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;
|
||||||
|
}
|
||||||
export const tsbuildProjectsLocation = "/user/username/projects";
|
export const tsbuildProjectsLocation = "/user/username/projects";
|
||||||
export function getTsBuildProjectFilePath(project: string, file: string) {
|
export function getTsBuildProjectFilePath(project: string, file: string) {
|
||||||
return `${tsbuildProjectsLocation}/${project}/${file}`;
|
return `${tsbuildProjectsLocation}/${project}/${file}`;
|
||||||
|
|
|
@ -1777,6 +1777,12 @@ namespace ts.server {
|
||||||
configFileErrors.push(...parsedCommandLine.errors);
|
configFileErrors.push(...parsedCommandLine.errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.info(`Config: ${configFilename} : ${JSON.stringify({
|
||||||
|
rootNames: parsedCommandLine.fileNames,
|
||||||
|
options: parsedCommandLine.options,
|
||||||
|
projectReferences: parsedCommandLine.projectReferences
|
||||||
|
}, /*replacer*/ undefined, " ")}`);
|
||||||
|
|
||||||
Debug.assert(!!parsedCommandLine.fileNames);
|
Debug.assert(!!parsedCommandLine.fileNames);
|
||||||
const compilerOptions = parsedCommandLine.options;
|
const compilerOptions = parsedCommandLine.options;
|
||||||
|
|
||||||
|
@ -1818,7 +1824,7 @@ namespace ts.server {
|
||||||
let scriptInfo: ScriptInfo | NormalizedPath;
|
let scriptInfo: ScriptInfo | NormalizedPath;
|
||||||
let path: Path;
|
let path: Path;
|
||||||
// Use the project's fileExists so that it can use caching instead of reaching to disk for the query
|
// Use the project's fileExists so that it can use caching instead of reaching to disk for the query
|
||||||
if (!isDynamic && !project.fileExists(newRootFile)) {
|
if (!isDynamic && !project.fileExistsWithCache(newRootFile)) {
|
||||||
path = normalizedPathToPath(normalizedPath, this.currentDirectory, this.toCanonicalFileName);
|
path = normalizedPathToPath(normalizedPath, this.currentDirectory, this.toCanonicalFileName);
|
||||||
const existingValue = projectRootFilesMap.get(path)!;
|
const existingValue = projectRootFilesMap.get(path)!;
|
||||||
if (isScriptInfo(existingValue)) {
|
if (isScriptInfo(existingValue)) {
|
||||||
|
@ -1851,7 +1857,7 @@ namespace ts.server {
|
||||||
projectRootFilesMap.forEach((value, path) => {
|
projectRootFilesMap.forEach((value, path) => {
|
||||||
if (!newRootScriptInfoMap.has(path)) {
|
if (!newRootScriptInfoMap.has(path)) {
|
||||||
if (isScriptInfo(value)) {
|
if (isScriptInfo(value)) {
|
||||||
project.removeFile(value, project.fileExists(path), /*detachFromProject*/ true);
|
project.removeFile(value, project.fileExistsWithCache(path), /*detachFromProject*/ true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
projectRootFilesMap.delete(path);
|
projectRootFilesMap.delete(path);
|
||||||
|
@ -2584,7 +2590,9 @@ namespace ts.server {
|
||||||
|
|
||||||
/*@internal*/
|
/*@internal*/
|
||||||
getOriginalLocationEnsuringConfiguredProject(project: Project, location: DocumentPosition): DocumentPosition | undefined {
|
getOriginalLocationEnsuringConfiguredProject(project: Project, location: DocumentPosition): DocumentPosition | undefined {
|
||||||
const originalLocation = project.getSourceMapper().tryGetSourcePosition(location);
|
const originalLocation = project.isSourceOfProjectReferenceRedirect(location.fileName) ?
|
||||||
|
location :
|
||||||
|
project.getSourceMapper().tryGetSourcePosition(location);
|
||||||
if (!originalLocation) return undefined;
|
if (!originalLocation) return undefined;
|
||||||
|
|
||||||
const { fileName } = originalLocation;
|
const { fileName } = originalLocation;
|
||||||
|
@ -2595,7 +2603,8 @@ namespace ts.server {
|
||||||
if (!configFileName) return undefined;
|
if (!configFileName) return undefined;
|
||||||
|
|
||||||
const configuredProject = this.findConfiguredProjectByProjectName(configFileName) ||
|
const configuredProject = this.findConfiguredProjectByProjectName(configFileName) ||
|
||||||
this.createAndLoadConfiguredProject(configFileName, `Creating project for original file: ${originalFileInfo.fileName} for location: ${location.fileName}`);
|
this.createAndLoadConfiguredProject(configFileName, `Creating project for original file: ${originalFileInfo.fileName}${location !== originalLocation ? " for location: " + location.fileName : ""}`);
|
||||||
|
if (configuredProject === project) return originalLocation;
|
||||||
updateProjectIfDirty(configuredProject);
|
updateProjectIfDirty(configuredProject);
|
||||||
// Keep this configured project as referenced from project
|
// Keep this configured project as referenced from project
|
||||||
addOriginalConfiguredProject(configuredProject);
|
addOriginalConfiguredProject(configuredProject);
|
||||||
|
|
|
@ -196,6 +196,11 @@ namespace ts.server {
|
||||||
/*@internal*/
|
/*@internal*/
|
||||||
originalConfiguredProjects: Map<true> | undefined;
|
originalConfiguredProjects: Map<true> | undefined;
|
||||||
|
|
||||||
|
/*@internal*/
|
||||||
|
getResolvedProjectReferenceToRedirect(_fileName: string): ResolvedProjectReference | undefined {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly cancellationToken: ThrottledCancellationToken;
|
private readonly cancellationToken: ThrottledCancellationToken;
|
||||||
|
|
||||||
public isNonTsProject() {
|
public isNonTsProject() {
|
||||||
|
@ -391,6 +396,11 @@ namespace ts.server {
|
||||||
}
|
}
|
||||||
|
|
||||||
fileExists(file: string): boolean {
|
fileExists(file: string): boolean {
|
||||||
|
return this.fileExistsWithCache(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @internal */
|
||||||
|
fileExistsWithCache(file: string): boolean {
|
||||||
// As an optimization, don't hit the disks for files we already know don't exist
|
// As an optimization, don't hit the disks for files we already know don't exist
|
||||||
// (because we're watching for their creation).
|
// (because we're watching for their creation).
|
||||||
const path = this.toPath(file);
|
const path = this.toPath(file);
|
||||||
|
@ -527,8 +537,11 @@ namespace ts.server {
|
||||||
return this.projectService.getSourceFileLike(fileName, this);
|
return this.projectService.getSourceFileLike(fileName, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private shouldEmitFile(scriptInfo: ScriptInfo) {
|
/*@internal*/
|
||||||
return scriptInfo && !scriptInfo.isDynamicOrHasMixedContent();
|
shouldEmitFile(scriptInfo: ScriptInfo | undefined) {
|
||||||
|
return scriptInfo &&
|
||||||
|
!scriptInfo.isDynamicOrHasMixedContent() &&
|
||||||
|
!this.program!.isSourceOfProjectReferenceRedirect(scriptInfo.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[] {
|
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[] {
|
||||||
|
@ -538,7 +551,7 @@ namespace ts.server {
|
||||||
updateProjectIfDirty(this);
|
updateProjectIfDirty(this);
|
||||||
this.builderState = BuilderState.create(this.program!, this.projectService.toCanonicalFileName, this.builderState);
|
this.builderState = BuilderState.create(this.program!, this.projectService.toCanonicalFileName, this.builderState);
|
||||||
return mapDefined(BuilderState.getFilesAffectedBy(this.builderState, this.program!, scriptInfo.path, this.cancellationToken, data => this.projectService.host.createHash!(data)), // TODO: GH#18217
|
return mapDefined(BuilderState.getFilesAffectedBy(this.builderState, this.program!, scriptInfo.path, this.cancellationToken, data => this.projectService.host.createHash!(data)), // TODO: GH#18217
|
||||||
sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)!) ? sourceFile.fileName : undefined);
|
sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1223,6 +1236,11 @@ namespace ts.server {
|
||||||
this.rootFilesMap.delete(info.path);
|
this.rootFilesMap.delete(info.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*@internal*/
|
||||||
|
isSourceOfProjectReferenceRedirect(fileName: string) {
|
||||||
|
return !!this.program && this.program.isSourceOfProjectReferenceRedirect(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
protected enableGlobalPlugins(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined) {
|
protected enableGlobalPlugins(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined) {
|
||||||
const host = this.projectService.host;
|
const host = this.projectService.host;
|
||||||
|
|
||||||
|
@ -1475,6 +1493,8 @@ namespace ts.server {
|
||||||
configFileWatcher: FileWatcher | undefined;
|
configFileWatcher: FileWatcher | undefined;
|
||||||
private directoriesWatchedForWildcards: Map<WildcardDirectoryWatcher> | undefined;
|
private directoriesWatchedForWildcards: Map<WildcardDirectoryWatcher> | undefined;
|
||||||
readonly canonicalConfigFilePath: NormalizedPath;
|
readonly canonicalConfigFilePath: NormalizedPath;
|
||||||
|
private projectReferenceCallbacks: ResolvedProjectReferenceCallbacks | undefined;
|
||||||
|
private mapOfDeclarationDirectories: Map<true> | undefined;
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
pendingReload: ConfigFileProgramReloadLevel | undefined;
|
pendingReload: ConfigFileProgramReloadLevel | undefined;
|
||||||
|
@ -1520,6 +1540,63 @@ namespace ts.server {
|
||||||
this.canonicalConfigFilePath = asNormalizedPath(projectService.toCanonicalFileName(configFileName));
|
this.canonicalConfigFilePath = asNormalizedPath(projectService.toCanonicalFileName(configFileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* @internal */
|
||||||
|
setResolvedProjectReferenceCallbacks(projectReferenceCallbacks: ResolvedProjectReferenceCallbacks) {
|
||||||
|
this.projectReferenceCallbacks = projectReferenceCallbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @internal */
|
||||||
|
useSourceOfProjectReferenceRedirect = () => !!this.languageServiceEnabled &&
|
||||||
|
!this.getCompilerOptions().disableSourceOfProjectReferenceRedirect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation of fileExists checks if the file being requested is
|
||||||
|
* .d.ts file for the referenced Project.
|
||||||
|
* If it is it returns true irrespective of whether that file exists on host
|
||||||
|
*/
|
||||||
|
fileExists(file: string): boolean {
|
||||||
|
// Project references go to source file instead of .d.ts file
|
||||||
|
if (this.useSourceOfProjectReferenceRedirect() && this.projectReferenceCallbacks) {
|
||||||
|
const source = this.projectReferenceCallbacks.getSourceOfProjectReferenceRedirect(file);
|
||||||
|
if (source) return isString(source) ? super.fileExists(source) : true;
|
||||||
|
}
|
||||||
|
return super.fileExists(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation of directoryExists checks if the directory being requested is
|
||||||
|
* directory of .d.ts file for the referenced Project.
|
||||||
|
* If it is it returns true irrespective of whether that directory exists on host
|
||||||
|
*/
|
||||||
|
directoryExists(path: string): boolean {
|
||||||
|
if (super.directoryExists(path)) return true;
|
||||||
|
if (!this.useSourceOfProjectReferenceRedirect() || !this.projectReferenceCallbacks) return false;
|
||||||
|
|
||||||
|
if (!this.mapOfDeclarationDirectories) {
|
||||||
|
this.mapOfDeclarationDirectories = createMap();
|
||||||
|
this.projectReferenceCallbacks.forEachResolvedProjectReference(ref => {
|
||||||
|
if (!ref) return;
|
||||||
|
const out = ref.commandLine.options.outFile || ref.commandLine.options.outDir;
|
||||||
|
if (out) {
|
||||||
|
this.mapOfDeclarationDirectories!.set(getDirectoryPath(this.toPath(out)), true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Set declaration's in different locations only, if they are next to source the directory present doesnt change
|
||||||
|
const declarationDir = ref.commandLine.options.declarationDir || ref.commandLine.options.outDir;
|
||||||
|
if (declarationDir) {
|
||||||
|
this.mapOfDeclarationDirectories!.set(this.toPath(declarationDir), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const dirPath = this.toPath(path);
|
||||||
|
const dirPathWithTrailingDirectorySeparator = `${dirPath}${directorySeparator}`;
|
||||||
|
return !!forEachKey(
|
||||||
|
this.mapOfDeclarationDirectories,
|
||||||
|
declDirPath => dirPath === declDirPath || startsWith(declDirPath, dirPathWithTrailingDirectorySeparator)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph
|
* If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph
|
||||||
* @returns: true if set of files in the project stays the same and false - otherwise.
|
* @returns: true if set of files in the project stays the same and false - otherwise.
|
||||||
|
@ -1528,6 +1605,8 @@ namespace ts.server {
|
||||||
this.isInitialLoadPending = returnFalse;
|
this.isInitialLoadPending = returnFalse;
|
||||||
const reloadLevel = this.pendingReload;
|
const reloadLevel = this.pendingReload;
|
||||||
this.pendingReload = ConfigFileProgramReloadLevel.None;
|
this.pendingReload = ConfigFileProgramReloadLevel.None;
|
||||||
|
this.projectReferenceCallbacks = undefined;
|
||||||
|
this.mapOfDeclarationDirectories = undefined;
|
||||||
let result: boolean;
|
let result: boolean;
|
||||||
switch (reloadLevel) {
|
switch (reloadLevel) {
|
||||||
case ConfigFileProgramReloadLevel.Partial:
|
case ConfigFileProgramReloadLevel.Partial:
|
||||||
|
@ -1570,6 +1649,12 @@ namespace ts.server {
|
||||||
return program && program.forEachResolvedProjectReference(cb);
|
return program && program.forEachResolvedProjectReference(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*@internal*/
|
||||||
|
getResolvedProjectReferenceToRedirect(fileName: string): ResolvedProjectReference | undefined {
|
||||||
|
const program = this.getCurrentProgram();
|
||||||
|
return program && program.getResolvedProjectReferenceToRedirect(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
/*@internal*/
|
/*@internal*/
|
||||||
enablePluginsWithOptions(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined) {
|
enablePluginsWithOptions(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined) {
|
||||||
const host = this.projectService.host;
|
const host = this.projectService.host;
|
||||||
|
@ -1652,6 +1737,8 @@ namespace ts.server {
|
||||||
this.stopWatchingWildCards();
|
this.stopWatchingWildCards();
|
||||||
this.projectErrors = undefined;
|
this.projectErrors = undefined;
|
||||||
this.configFileSpecs = undefined;
|
this.configFileSpecs = undefined;
|
||||||
|
this.projectReferenceCallbacks = undefined;
|
||||||
|
this.mapOfDeclarationDirectories = undefined;
|
||||||
super.close();
|
super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -495,15 +495,17 @@ namespace ts.server {
|
||||||
// the default project; if no configured projects, the first external project should
|
// the default project; if no configured projects, the first external project should
|
||||||
// be the default project; otherwise the first inferred project should be the default.
|
// be the default project; otherwise the first inferred project should be the default.
|
||||||
let firstExternalProject;
|
let firstExternalProject;
|
||||||
|
let firstConfiguredProject;
|
||||||
for (const project of this.containingProjects) {
|
for (const project of this.containingProjects) {
|
||||||
if (project.projectKind === ProjectKind.Configured) {
|
if (project.projectKind === ProjectKind.Configured) {
|
||||||
return project;
|
if (!project.isSourceOfProjectReferenceRedirect(this.fileName)) return project;
|
||||||
|
if (!firstConfiguredProject) firstConfiguredProject = project;
|
||||||
}
|
}
|
||||||
else if (project.projectKind === ProjectKind.External && !firstExternalProject) {
|
else if (project.projectKind === ProjectKind.External && !firstExternalProject) {
|
||||||
firstExternalProject = project;
|
firstExternalProject = project;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return firstExternalProject || this.containingProjects[0];
|
return firstConfiguredProject || firstExternalProject || this.containingProjects[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -448,7 +448,9 @@ namespace ts.server {
|
||||||
|
|
||||||
function getDefinitionInProject(definition: DocumentPosition | undefined, definingProject: Project, project: Project): DocumentPosition | undefined {
|
function getDefinitionInProject(definition: DocumentPosition | undefined, definingProject: Project, project: Project): DocumentPosition | undefined {
|
||||||
if (!definition || project.containsFile(toNormalizedPath(definition.fileName))) return definition;
|
if (!definition || project.containsFile(toNormalizedPath(definition.fileName))) return definition;
|
||||||
const mappedDefinition = definingProject.getLanguageService().getSourceMapper().tryGetGeneratedPosition(definition);
|
const mappedDefinition = definingProject.isSourceOfProjectReferenceRedirect(definition.fileName) ?
|
||||||
|
definition :
|
||||||
|
definingProject.getLanguageService().getSourceMapper().tryGetGeneratedPosition(definition);
|
||||||
return mappedDefinition && project.containsFile(toNormalizedPath(mappedDefinition.fileName)) ? mappedDefinition : undefined;
|
return mappedDefinition && project.containsFile(toNormalizedPath(mappedDefinition.fileName)) ? mappedDefinition : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,7 +479,7 @@ namespace ts.server {
|
||||||
for (const symlinkedProject of symlinkedProjects) addToTodo({ project: symlinkedProject, location: originalLocation as TLocation }, toDo!, seenProjects);
|
for (const symlinkedProject of symlinkedProjects) addToTodo({ project: symlinkedProject, location: originalLocation as TLocation }, toDo!, seenProjects);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return originalLocation;
|
return originalLocation === location ? undefined : originalLocation;
|
||||||
});
|
});
|
||||||
return toDo;
|
return toDo;
|
||||||
}
|
}
|
||||||
|
@ -1037,7 +1039,9 @@ namespace ts.server {
|
||||||
|
|
||||||
private getEmitOutput(args: protocol.FileRequestArgs): EmitOutput {
|
private getEmitOutput(args: protocol.FileRequestArgs): EmitOutput {
|
||||||
const { file, project } = this.getFileAndProject(args);
|
const { file, project } = this.getFileAndProject(args);
|
||||||
return project.getLanguageService().getEmitOutput(file);
|
return project.shouldEmitFile(project.getScriptInfo(file)) ?
|
||||||
|
project.getLanguageService().getEmitOutput(file) :
|
||||||
|
{ emitSkipped: true, outputFiles: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapDefinitionInfo(definitions: readonly DefinitionInfo[], project: Project): readonly protocol.FileSpanWithContext[] {
|
private mapDefinitionInfo(definitions: readonly DefinitionInfo[], project: Project): readonly protocol.FileSpanWithContext[] {
|
||||||
|
@ -1672,10 +1676,10 @@ namespace ts.server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createCheckList(fileNames: string[], defaultProject?: Project): PendingErrorCheck[] {
|
private createCheckList(fileNames: string[]): PendingErrorCheck[] {
|
||||||
return mapDefined<string, PendingErrorCheck>(fileNames, uncheckedFileName => {
|
return mapDefined<string, PendingErrorCheck>(fileNames, uncheckedFileName => {
|
||||||
const fileName = toNormalizedPath(uncheckedFileName);
|
const fileName = toNormalizedPath(uncheckedFileName);
|
||||||
const project = defaultProject || this.projectService.tryGetDefaultProjectForFile(fileName);
|
const project = this.projectService.tryGetDefaultProjectForFile(fileName);
|
||||||
return project && { fileName, project };
|
return project && { fileName, project };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1149,10 +1149,10 @@ namespace ts {
|
||||||
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
||||||
getCurrentDirectory: () => currentDirectory,
|
getCurrentDirectory: () => currentDirectory,
|
||||||
getProgram,
|
getProgram,
|
||||||
fileExists: host.fileExists && (f => host.fileExists!(f)),
|
fileExists: maybeBind(host, host.fileExists),
|
||||||
readFile: host.readFile && ((f, encoding) => host.readFile!(f, encoding)),
|
readFile: maybeBind(host, host.readFile),
|
||||||
getDocumentPositionMapper: host.getDocumentPositionMapper && ((generatedFileName, sourceFileName) => host.getDocumentPositionMapper!(generatedFileName, sourceFileName)),
|
getDocumentPositionMapper: maybeBind(host, host.getDocumentPositionMapper),
|
||||||
getSourceFileLike: host.getSourceFileLike && (f => host.getSourceFileLike!(f)),
|
getSourceFileLike: maybeBind(host, host.getSourceFileLike),
|
||||||
log
|
log
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1250,6 +1250,12 @@ namespace ts {
|
||||||
if (host.resolveTypeReferenceDirectives) {
|
if (host.resolveTypeReferenceDirectives) {
|
||||||
compilerHost.resolveTypeReferenceDirectives = (...args) => host.resolveTypeReferenceDirectives!(...args);
|
compilerHost.resolveTypeReferenceDirectives = (...args) => host.resolveTypeReferenceDirectives!(...args);
|
||||||
}
|
}
|
||||||
|
if (host.setResolvedProjectReferenceCallbacks) {
|
||||||
|
compilerHost.setResolvedProjectReferenceCallbacks = callbacks => host.setResolvedProjectReferenceCallbacks!(callbacks);
|
||||||
|
}
|
||||||
|
if (host.useSourceOfProjectReferenceRedirect) {
|
||||||
|
compilerHost.useSourceOfProjectReferenceRedirect = () => host.useSourceOfProjectReferenceRedirect!();
|
||||||
|
}
|
||||||
|
|
||||||
const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
|
const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
|
||||||
const options: CreateProgramOptions = {
|
const options: CreateProgramOptions = {
|
||||||
|
|
|
@ -70,6 +70,11 @@ namespace ts {
|
||||||
if (!sourceFile) return undefined;
|
if (!sourceFile) return undefined;
|
||||||
|
|
||||||
const program = host.getProgram()!;
|
const program = host.getProgram()!;
|
||||||
|
// If this is source file of project reference source (instead of redirect) there is no generated position
|
||||||
|
if (program.isSourceOfProjectReferenceRedirect(sourceFile.fileName)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const options = program.getCompilerOptions();
|
const options = program.getCompilerOptions();
|
||||||
const outPath = options.outFile || options.out;
|
const outPath = options.outFile || options.out;
|
||||||
|
|
||||||
|
|
|
@ -234,6 +234,10 @@ namespace ts {
|
||||||
getDocumentPositionMapper?(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined;
|
getDocumentPositionMapper?(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined;
|
||||||
/* @internal */
|
/* @internal */
|
||||||
getSourceFileLike?(fileName: string): SourceFileLike | undefined;
|
getSourceFileLike?(fileName: string): SourceFileLike | undefined;
|
||||||
|
/* @internal */
|
||||||
|
setResolvedProjectReferenceCallbacks?(callbacks: ResolvedProjectReferenceCallbacks): void;
|
||||||
|
/* @internal */
|
||||||
|
useSourceOfProjectReferenceRedirect?(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
|
|
|
@ -145,6 +145,8 @@
|
||||||
"unittests/tsserver/occurences.ts",
|
"unittests/tsserver/occurences.ts",
|
||||||
"unittests/tsserver/openFile.ts",
|
"unittests/tsserver/openFile.ts",
|
||||||
"unittests/tsserver/projectErrors.ts",
|
"unittests/tsserver/projectErrors.ts",
|
||||||
|
"unittests/tsserver/projectReferenceCompileOnSave.ts",
|
||||||
|
"unittests/tsserver/projectReferenceErrors.ts",
|
||||||
"unittests/tsserver/projectReferences.ts",
|
"unittests/tsserver/projectReferences.ts",
|
||||||
"unittests/tsserver/projects.ts",
|
"unittests/tsserver/projects.ts",
|
||||||
"unittests/tsserver/refactors.ts",
|
"unittests/tsserver/refactors.ts",
|
||||||
|
|
|
@ -2,18 +2,12 @@ namespace ts.tscWatch {
|
||||||
import projectsLocation = TestFSWithWatch.tsbuildProjectsLocation;
|
import projectsLocation = TestFSWithWatch.tsbuildProjectsLocation;
|
||||||
import getFilePathInProject = TestFSWithWatch.getTsBuildProjectFilePath;
|
import getFilePathInProject = TestFSWithWatch.getTsBuildProjectFilePath;
|
||||||
import getFileFromProject = TestFSWithWatch.getTsBuildProjectFile;
|
import getFileFromProject = TestFSWithWatch.getTsBuildProjectFile;
|
||||||
type TsBuildWatchSystem = WatchedSystem & { writtenFiles: Map<true>; };
|
type TsBuildWatchSystem = TestFSWithWatch.TestServerHostTrackingWrittenFiles;
|
||||||
|
|
||||||
function createTsBuildWatchSystem(fileOrFolderList: readonly TestFSWithWatch.FileOrFolderOrSymLink[], params?: TestFSWithWatch.TestServerHostCreationParameters) {
|
function createTsBuildWatchSystem(fileOrFolderList: readonly TestFSWithWatch.FileOrFolderOrSymLink[], params?: TestFSWithWatch.TestServerHostCreationParameters) {
|
||||||
const host = createWatchedSystem(fileOrFolderList, params) as TsBuildWatchSystem;
|
return TestFSWithWatch.changeToHostTrackingWrittenFiles(
|
||||||
const originalWriteFile = host.writeFile;
|
createWatchedSystem(fileOrFolderList, params)
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSolutionBuilder(system: WatchedSystem, rootNames: readonly string[], defaultOptions?: BuildOptions) {
|
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 coreIndexDts = projectFileName(SubProject.core, "index.d.ts");
|
||||||
const coreAnotherModuleDts = projectFileName(SubProject.core, "anotherModule.d.ts");
|
const coreAnotherModuleDts = projectFileName(SubProject.core, "anotherModule.d.ts");
|
||||||
const logicIndexDts = projectFileName(SubProject.logic, "index.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 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];
|
const expectedProgramFiles = () => [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, logicIndexDts];
|
||||||
|
|
||||||
function createSolutionAndWatchMode() {
|
function createSolutionAndWatchMode() {
|
||||||
|
@ -723,12 +717,19 @@ let x: string = 10;`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyWatches(host: TsBuildWatchSystem, withTsserver?: boolean) {
|
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(
|
function verifyScenario(
|
||||||
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void,
|
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void,
|
||||||
expectedFilesAfterEdit: () => readonly string[]
|
expectedProgramFilesAfterEdit: () => readonly string[],
|
||||||
|
expectedProjectFilesAfterEdit: () => readonly string[]
|
||||||
) {
|
) {
|
||||||
it("with tsc-watch", () => {
|
it("with tsc-watch", () => {
|
||||||
const { host, solutionBuilder, watch } = createSolutionAndWatchMode();
|
const { host, solutionBuilder, watch } = createSolutionAndWatchMode();
|
||||||
|
@ -737,7 +738,7 @@ let x: string = 10;`);
|
||||||
|
|
||||||
host.checkTimeoutQueueLengthAndRun(1);
|
host.checkTimeoutQueueLengthAndRun(1);
|
||||||
checkOutputErrorsIncremental(host, emptyArray);
|
checkOutputErrorsIncremental(host, emptyArray);
|
||||||
checkProgramActualFiles(watch(), expectedFilesAfterEdit());
|
checkProgramActualFiles(watch(), expectedProgramFilesAfterEdit());
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -747,7 +748,7 @@ let x: string = 10;`);
|
||||||
edit(host, solutionBuilder);
|
edit(host, solutionBuilder);
|
||||||
|
|
||||||
host.checkTimeoutQueueLengthAndRun(2);
|
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
|
// 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
|
// 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", () => {
|
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.invalidateProject(logic[0].path.toLowerCase() as ResolvedConfigFilePath);
|
||||||
solutionBuilder.buildNextInvalidatedProject();
|
solutionBuilder.buildNextInvalidatedProject();
|
||||||
}, expectedProgramFiles);
|
}, expectedProgramFiles, expectedProjectFiles);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("change in project reference config file builds correctly", () => {
|
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.invalidateProject(logic[0].path.toLowerCase() as ResolvedConfigFilePath, ConfigFileProgramReloadLevel.Full);
|
||||||
solutionBuilder.buildNextInvalidatedProject();
|
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 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 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 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 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 ? [
|
const expectedWatchedDirectories = multiFolder ? [
|
||||||
getProjectPath(project).toLowerCase() // watches for directories created for resolution of b
|
getProjectPath(project).toLowerCase() // watches for directories created for resolution of b
|
||||||
] : emptyArray;
|
] : emptyArray;
|
||||||
|
@ -926,22 +929,29 @@ export function gfoo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyProject(host: TsBuildWatchSystem, service: projectSystem.TestProjectService, orphanInfos?: readonly string[]) {
|
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(
|
interface VerifyServerState {
|
||||||
host: TsBuildWatchSystem,
|
host: TsBuildWatchSystem;
|
||||||
service: projectSystem.TestProjectService,
|
service: projectSystem.TestProjectService;
|
||||||
expectedProgramFiles: readonly string[],
|
expectedProjectFiles: readonly string[];
|
||||||
expectedWatchedFiles: readonly string[],
|
expectedProjectWatchedFiles: readonly string[];
|
||||||
expectedWatchedDirectoriesRecursive: readonly string[],
|
expectedWatchedDirectoriesRecursive: readonly string[];
|
||||||
orphanInfos?: readonly string[]) {
|
orphanInfos?: readonly string[];
|
||||||
checkProjectActualFiles(service, cTsconfig.path, expectedProgramFiles.concat(cTsconfig.path));
|
}
|
||||||
const watchedFiles = expectedWatchedFiles.filter(f => f !== cTs.path.toLowerCase());
|
function verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos }: VerifyServerState) {
|
||||||
if (orphanInfos) {
|
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) {
|
for (const orphan of orphanInfos) {
|
||||||
const info = service.getScriptInfoForPath(orphan as Path);
|
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);
|
assert.equal(info!.containingProjects.length, 0);
|
||||||
watchedFiles.push(orphan);
|
watchedFiles.push(orphan);
|
||||||
}
|
}
|
||||||
|
@ -949,16 +959,20 @@ export function gfoo() {
|
||||||
verifyWatchesOfProject(host, watchedFiles, expectedWatchedDirectoriesRecursive, expectedWatchedDirectories);
|
verifyWatchesOfProject(host, watchedFiles, expectedWatchedDirectoriesRecursive, expectedWatchedDirectories);
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyScenario(
|
interface VerifyScenario {
|
||||||
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void,
|
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram>) => void;
|
||||||
expectedEditErrors: readonly string[],
|
expectedEditErrors: readonly string[];
|
||||||
expectedProgramFiles: readonly string[],
|
expectedProgramFiles: readonly string[];
|
||||||
expectedWatchedFiles: readonly string[],
|
expectedProjectFiles: readonly string[];
|
||||||
expectedWatchedDirectoriesRecursive: readonly string[],
|
expectedWatchedFiles: readonly string[];
|
||||||
dependencies: readonly [string, readonly string[]][],
|
expectedProjectWatchedFiles: readonly string[];
|
||||||
revert?: (host: TsBuildWatchSystem) => void,
|
expectedWatchedDirectoriesRecursive: readonly string[];
|
||||||
orphanInfosAfterEdit?: readonly string[],
|
dependencies: readonly [string, readonly string[]][];
|
||||||
orphanInfosAfterRevert?: 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", () => {
|
it("with tsc-watch", () => {
|
||||||
const { host, solutionBuilder, watch } = createSolutionAndWatchMode();
|
const { host, solutionBuilder, watch } = createSolutionAndWatchMode();
|
||||||
|
|
||||||
|
@ -985,7 +999,7 @@ export function gfoo() {
|
||||||
edit(host, solutionBuilder);
|
edit(host, solutionBuilder);
|
||||||
|
|
||||||
host.checkTimeoutQueueLengthAndRun(2);
|
host.checkTimeoutQueueLengthAndRun(2);
|
||||||
verifyServerState(host, service, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfosAfterEdit);
|
verifyServerState({ host, service, expectedProjectFiles, expectedProjectWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos: orphanInfosAfterEdit });
|
||||||
|
|
||||||
if (revert) {
|
if (revert) {
|
||||||
revert(host);
|
revert(host);
|
||||||
|
@ -1010,20 +1024,21 @@ export function gfoo() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("non local edit updates the program and watch correctly", () => {
|
describe("non local edit updates the program and watch correctly", () => {
|
||||||
verifyScenario(
|
verifyScenario({
|
||||||
(host, solutionBuilder) => {
|
edit: (host, solutionBuilder) => {
|
||||||
// edit
|
// edit
|
||||||
host.writeFile(bTs.path, `${bTs.content}
|
host.writeFile(bTs.path, `${bTs.content}\nexport function gfoo() {\n}`);
|
||||||
export function gfoo() {
|
solutionBuilder.invalidateProject((bTsconfig.path.toLowerCase() as ResolvedConfigFilePath));
|
||||||
}`);
|
|
||||||
solutionBuilder.invalidateProject(bTsconfig.path.toLowerCase() as ResolvedConfigFilePath);
|
|
||||||
solutionBuilder.buildNextInvalidatedProject();
|
solutionBuilder.buildNextInvalidatedProject();
|
||||||
},
|
},
|
||||||
emptyArray,
|
expectedEditErrors: emptyArray,
|
||||||
expectedProgramFiles,
|
expectedProgramFiles,
|
||||||
|
expectedProjectFiles,
|
||||||
expectedWatchedFiles,
|
expectedWatchedFiles,
|
||||||
|
expectedProjectWatchedFiles,
|
||||||
expectedWatchedDirectoriesRecursive,
|
expectedWatchedDirectoriesRecursive,
|
||||||
defaultDependencies);
|
dependencies: defaultDependencies
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("edit on config file", () => {
|
describe("edit on config file", () => {
|
||||||
|
@ -1032,30 +1047,32 @@ export function gfoo() {
|
||||||
path: getFilePathInProject(project, "nrefs/a.d.ts"),
|
path: getFilePathInProject(project, "nrefs/a.d.ts"),
|
||||||
content: refs.content
|
content: refs.content
|
||||||
};
|
};
|
||||||
verifyScenario(
|
verifyScenario({
|
||||||
host => {
|
edit: host => {
|
||||||
const cTsConfigJson = JSON.parse(cTsconfig.content);
|
const cTsConfigJson = JSON.parse(cTsconfig.content);
|
||||||
host.ensureFileOrFolder(nrefs);
|
host.ensureFileOrFolder(nrefs);
|
||||||
cTsConfigJson.compilerOptions.paths = { "@ref/*": nrefsPath };
|
cTsConfigJson.compilerOptions.paths = { "@ref/*": nrefsPath };
|
||||||
host.writeFile(cTsconfig.path, JSON.stringify(cTsConfigJson));
|
host.writeFile(cTsconfig.path, JSON.stringify(cTsConfigJson));
|
||||||
},
|
},
|
||||||
emptyArray,
|
expectedEditErrors: emptyArray,
|
||||||
expectedProgramFiles.map(nrefReplacer),
|
expectedProgramFiles: expectedProgramFiles.map(nrefReplacer),
|
||||||
expectedWatchedFiles.map(nrefReplacer),
|
expectedProjectFiles: expectedProjectFiles.map(nrefReplacer),
|
||||||
expectedWatchedDirectoriesRecursive.map(nrefReplacer),
|
expectedWatchedFiles: expectedWatchedFiles.map(nrefReplacer),
|
||||||
[
|
expectedProjectWatchedFiles: expectedProjectWatchedFiles.map(nrefReplacer),
|
||||||
|
expectedWatchedDirectoriesRecursive: expectedWatchedDirectoriesRecursive.map(nrefReplacer),
|
||||||
|
dependencies: [
|
||||||
[aDts, [aDts]],
|
[aDts, [aDts]],
|
||||||
[bDts, [bDts, aDts]],
|
[bDts, [bDts, aDts]],
|
||||||
[nrefs.path, [nrefs.path]],
|
[nrefs.path, [nrefs.path]],
|
||||||
[cTs.path, [cTs.path, nrefs.path, bDts]]
|
[cTs.path, [cTs.path, nrefs.path, bDts]]
|
||||||
],
|
],
|
||||||
// revert the update
|
// 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
|
// 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
|
// 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", () => {
|
describe("edit in referenced config file", () => {
|
||||||
|
@ -1064,82 +1081,84 @@ export function gfoo() {
|
||||||
content: "export declare class A {}"
|
content: "export declare class A {}"
|
||||||
};
|
};
|
||||||
const expectedProgramFiles = [cTs.path, bDts, nrefs.path, refs.path, libFile.path];
|
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
|
const [, ...expectedWatchedDirectoriesRecursiveWithoutA] = expectedWatchedDirectoriesRecursive; // Not looking in a folder for resolution in multi folder scenario
|
||||||
verifyScenario(
|
verifyScenario({
|
||||||
host => {
|
edit: host => {
|
||||||
const bTsConfigJson = JSON.parse(bTsconfig.content);
|
const bTsConfigJson = JSON.parse(bTsconfig.content);
|
||||||
host.ensureFileOrFolder(nrefs);
|
host.ensureFileOrFolder(nrefs);
|
||||||
bTsConfigJson.compilerOptions.paths = { "@ref/*": nrefsPath };
|
bTsConfigJson.compilerOptions.paths = { "@ref/*": nrefsPath };
|
||||||
host.writeFile(bTsconfig.path, JSON.stringify(bTsConfigJson));
|
host.writeFile(bTsconfig.path, JSON.stringify(bTsConfigJson));
|
||||||
},
|
},
|
||||||
emptyArray,
|
expectedEditErrors: emptyArray,
|
||||||
expectedProgramFiles,
|
expectedProgramFiles,
|
||||||
expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path, aTsconfig.path).map(s => s.toLowerCase()),
|
expectedProjectFiles,
|
||||||
(multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive).concat(getFilePathInProject(project, "nrefs").toLowerCase()),
|
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]],
|
[nrefs.path, [nrefs.path]],
|
||||||
[bDts, [bDts, nrefs.path]],
|
[bDts, [bDts, nrefs.path]],
|
||||||
[refs.path, [refs.path]],
|
[refs.path, [refs.path]],
|
||||||
[cTs.path, [cTs.path, refs.path, bDts]],
|
[cTs.path, [cTs.path, refs.path, bDts]],
|
||||||
],
|
],
|
||||||
// revert the update
|
// 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
|
// 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
|
// 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", () => {
|
describe("deleting referenced config file", () => {
|
||||||
const expectedProgramFiles = [cTs.path, bTs.path, refs.path, libFile.path];
|
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
|
const [, ...expectedWatchedDirectoriesRecursiveWithoutA] = expectedWatchedDirectoriesRecursive; // Not looking in a folder for resolution in multi folder scenario
|
||||||
// Resolutions should change now
|
// Resolutions should change now
|
||||||
// Should map to b.ts instead with options from our own config
|
// Should map to b.ts instead with options from our own config
|
||||||
verifyScenario(
|
verifyScenario({
|
||||||
host => host.deleteFile(bTsconfig.path),
|
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`
|
`${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,
|
||||||
expectedProgramFiles.concat(cTsconfig.path, bTsconfig.path).map(s => s.toLowerCase()),
|
expectedProjectFiles: expectedProgramFiles,
|
||||||
multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive,
|
expectedWatchedFiles,
|
||||||
[
|
expectedProjectWatchedFiles: expectedWatchedFiles,
|
||||||
|
expectedWatchedDirectoriesRecursive: multiFolder ? expectedWatchedDirectoriesRecursiveWithoutA : expectedWatchedDirectoriesRecursive,
|
||||||
|
dependencies: [
|
||||||
[bTs.path, [bTs.path, refs.path]],
|
[bTs.path, [bTs.path, refs.path]],
|
||||||
[refs.path, [refs.path]],
|
[refs.path, [refs.path]],
|
||||||
[cTs.path, [cTs.path, refs.path, bTs.path]],
|
[cTs.path, [cTs.path, refs.path, bTs.path]],
|
||||||
],
|
],
|
||||||
// revert the update
|
// 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
|
// AfterEdit:: Extra watched files on server since the script infos arent deleted till next file open
|
||||||
[bDts.toLowerCase(), aDts.toLowerCase(), aTsconfig.path.toLowerCase()],
|
orphanInfosAfterEdit: [aTs.path.toLowerCase(), aTsconfig.path.toLowerCase()],
|
||||||
// AfterRevert:: Extra watched files on server since the script infos arent deleted till next file open
|
});
|
||||||
[bTs.path.toLowerCase()]
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("deleting transitively referenced config file", () => {
|
describe("deleting transitively referenced config file", () => {
|
||||||
verifyScenario(
|
verifyScenario({
|
||||||
host => host.deleteFile(aTsconfig.path),
|
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`
|
`${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)),
|
expectedProgramFiles: expectedProgramFiles.map(s => s.replace(aDts, aTs.path)),
|
||||||
expectedWatchedFiles.map(s => s.replace(aDts.toLowerCase(), aTs.path.toLocaleLowerCase())),
|
expectedProjectFiles,
|
||||||
|
expectedWatchedFiles: expectedWatchedFiles.map(s => s.replace(aDts.toLowerCase(), aTs.path.toLocaleLowerCase())),
|
||||||
|
expectedProjectWatchedFiles,
|
||||||
expectedWatchedDirectoriesRecursive,
|
expectedWatchedDirectoriesRecursive,
|
||||||
[
|
dependencies: [
|
||||||
[aTs.path, [aTs.path]],
|
[aTs.path, [aTs.path]],
|
||||||
[bDts, [bDts, aTs.path]],
|
[bDts, [bDts, aTs.path]],
|
||||||
[refs.path, [refs.path]],
|
[refs.path, [refs.path]],
|
||||||
[cTs.path, [cTs.path, refs.path, bDts]],
|
[cTs.path, [cTs.path, refs.path, bDts]],
|
||||||
],
|
],
|
||||||
// revert the update
|
// revert the update
|
||||||
host => host.writeFile(aTsconfig.path, aTsconfig.content),
|
revert: 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()]
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,7 @@ namespace ts.projectSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyUserTsConfigProject(session: TestSession) {
|
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", () => {
|
it("goToDefinition", () => {
|
||||||
|
@ -450,6 +450,13 @@ namespace ts.projectSystem {
|
||||||
name: "function f(): void",
|
name: "function f(): void",
|
||||||
},
|
},
|
||||||
references: [
|
references: [
|
||||||
|
makeReferenceEntry({
|
||||||
|
file: aTs,
|
||||||
|
text: "f",
|
||||||
|
options: { index: 1 },
|
||||||
|
contextText: "function f() {}",
|
||||||
|
isDefinition: true
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
fileName: bTs.path,
|
fileName: bTs.path,
|
||||||
isDefinition: false,
|
isDefinition: false,
|
||||||
|
@ -457,13 +464,6 @@ namespace ts.projectSystem {
|
||||||
isWriteAccess: false,
|
isWriteAccess: false,
|
||||||
textSpan: { start: 0, length: 1 },
|
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`);
|
verifyEvent(project, `Change in config file detected`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("when opening original location project", () => {
|
describe("when opening original location project", () => {
|
||||||
const aDTs: File = {
|
it("with project references", () => {
|
||||||
path: `${projectRoot}/a/a.d.ts`,
|
verify();
|
||||||
content: `export declare class A {
|
});
|
||||||
|
|
||||||
|
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
|
//# sourceMappingURL=a.d.ts.map
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
const aDTsMap: File = {
|
const aDTsMap: File = {
|
||||||
path: `${projectRoot}/a/a.d.ts.map`,
|
path: `${projectRoot}/a/a.d.ts.map`,
|
||||||
content: `{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["./a.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;CAAI"}`
|
content: `{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["./a.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;CAAI"}`
|
||||||
};
|
};
|
||||||
const bTs: File = {
|
const bTs: File = {
|
||||||
path: bTsPath,
|
path: bTsPath,
|
||||||
content: `import {A} from "../a/a"; new A();`
|
content: `import {A} from "../a/a"; new A();`
|
||||||
};
|
};
|
||||||
const configB: File = {
|
const configB: File = {
|
||||||
path: configBPath,
|
path: configBPath,
|
||||||
content: JSON.stringify({
|
content: JSON.stringify({
|
||||||
references: [{ path: "../a" }]
|
...(disableSourceOfProjectReferenceRedirect && {
|
||||||
})
|
compilerOptions: {
|
||||||
};
|
disableSourceOfProjectReferenceRedirect
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
references: [{ path: "../a" }]
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
const { service, session, verifyEventWithOpenTs, verifyEvent } = createSessionToVerifyEvent(files.concat(aDTs, aDTsMap, bTs, configB));
|
const { service, session, verifyEventWithOpenTs, verifyEvent } = createSessionToVerifyEvent(files.concat(aDTs, aDTsMap, bTs, configB));
|
||||||
verifyEventWithOpenTs(bTs, configB.path, 1);
|
verifyEventWithOpenTs(bTs, configB.path, 1);
|
||||||
|
|
||||||
session.executeCommandSeq<protocol.ReferencesRequest>({
|
session.executeCommandSeq<protocol.ReferencesRequest>({
|
||||||
command: protocol.CommandTypes.References,
|
command: protocol.CommandTypes.References,
|
||||||
arguments: {
|
arguments: {
|
||||||
file: bTs.path,
|
file: bTs.path,
|
||||||
...protocolLocationFromSubstring(bTs.content, "A()")
|
...protocolLocationFromSubstring(bTs.content, "A()")
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
checkNumberOfProjects(service, { configuredProjects: 2 });
|
checkNumberOfProjects(service, { configuredProjects: 2 });
|
||||||
const project = service.configuredProjects.get(configA.path)!;
|
const project = service.configuredProjects.get(configA.path)!;
|
||||||
assert.isDefined(project);
|
assert.isDefined(project);
|
||||||
verifyEvent(project, `Creating project for original file: ${aTs.path} for location: ${aDTs.path}`);
|
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 ", () => {
|
describe("with external projects and config files ", () => {
|
||||||
|
|
|
@ -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));
|
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[]) {
|
export function checkScriptInfos(projectService: server.ProjectService, expectedFiles: readonly string[], additionInfo?: string) {
|
||||||
checkArray("ScriptInfos files", arrayFrom(projectService.filenameToScriptInfo.values(), info => info.fileName), expectedFiles);
|
checkArray(`ScriptInfos files: ${additionInfo || ""}`, arrayFrom(projectService.filenameToScriptInfo.values(), info => info.fileName), expectedFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function protocolLocationFromSubstring(str: string, substring: string): protocol.Location {
|
export function protocolLocationFromSubstring(str: string, substring: string): protocol.Location {
|
||||||
|
@ -501,7 +501,7 @@ namespace ts.projectSystem {
|
||||||
return protocolToLocation(str)(start);
|
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);
|
const lineStarts = computeLineStarts(text);
|
||||||
return pos => {
|
return pos => {
|
||||||
const x = computeLineAndCharacterOfPosition(lineStarts, 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(),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
430
src/testRunner/unittests/tsserver/projectReferenceErrors.ts
Normal file
430
src/testRunner/unittests/tsserver/projectReferenceErrors.ts
Normal file
|
@ -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
|
@ -2567,6 +2567,7 @@ declare namespace ts {
|
||||||
emitDeclarationOnly?: boolean;
|
emitDeclarationOnly?: boolean;
|
||||||
declarationDir?: string;
|
declarationDir?: string;
|
||||||
disableSizeLimit?: boolean;
|
disableSizeLimit?: boolean;
|
||||||
|
disableSourceOfProjectReferenceRedirect?: boolean;
|
||||||
downlevelIteration?: boolean;
|
downlevelIteration?: boolean;
|
||||||
emitBOM?: boolean;
|
emitBOM?: boolean;
|
||||||
emitDecoratorMetadata?: boolean;
|
emitDecoratorMetadata?: boolean;
|
||||||
|
@ -8529,7 +8530,6 @@ declare namespace ts.server {
|
||||||
getGlobalProjectErrors(): readonly Diagnostic[];
|
getGlobalProjectErrors(): readonly Diagnostic[];
|
||||||
getAllProjectErrors(): readonly Diagnostic[];
|
getAllProjectErrors(): readonly Diagnostic[];
|
||||||
getLanguageService(ensureSynchronized?: boolean): LanguageService;
|
getLanguageService(ensureSynchronized?: boolean): LanguageService;
|
||||||
private shouldEmitFile;
|
|
||||||
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[];
|
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[];
|
||||||
/**
|
/**
|
||||||
* Returns true if emit was conducted
|
* Returns true if emit was conducted
|
||||||
|
@ -8610,11 +8610,25 @@ declare namespace ts.server {
|
||||||
private typeAcquisition;
|
private typeAcquisition;
|
||||||
private directoriesWatchedForWildcards;
|
private directoriesWatchedForWildcards;
|
||||||
readonly canonicalConfigFilePath: NormalizedPath;
|
readonly canonicalConfigFilePath: NormalizedPath;
|
||||||
|
private projectReferenceCallbacks;
|
||||||
|
private mapOfDeclarationDirectories;
|
||||||
/** Ref count to the project when opened from external project */
|
/** Ref count to the project when opened from external project */
|
||||||
private externalProjectRefCount;
|
private externalProjectRefCount;
|
||||||
private projectErrors;
|
private projectErrors;
|
||||||
private projectReferences;
|
private projectReferences;
|
||||||
protected isInitialLoadPending: () => boolean;
|
protected isInitialLoadPending: () => boolean;
|
||||||
|
/**
|
||||||
|
* This implementation of fileExists checks if the file being requested is
|
||||||
|
* .d.ts file for the referenced Project.
|
||||||
|
* If it is it returns true irrespective of whether that file exists on host
|
||||||
|
*/
|
||||||
|
fileExists(file: string): boolean;
|
||||||
|
/**
|
||||||
|
* This implementation of directoryExists checks if the directory being requested is
|
||||||
|
* directory of .d.ts file for the referenced Project.
|
||||||
|
* If it is it returns true irrespective of whether that directory exists on host
|
||||||
|
*/
|
||||||
|
directoryExists(path: string): boolean;
|
||||||
/**
|
/**
|
||||||
* If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph
|
* If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph
|
||||||
* @returns: true if set of files in the project stays the same and false - otherwise.
|
* @returns: true if set of files in the project stays the same and false - otherwise.
|
||||||
|
|
|
@ -2567,6 +2567,7 @@ declare namespace ts {
|
||||||
emitDeclarationOnly?: boolean;
|
emitDeclarationOnly?: boolean;
|
||||||
declarationDir?: string;
|
declarationDir?: string;
|
||||||
disableSizeLimit?: boolean;
|
disableSizeLimit?: boolean;
|
||||||
|
disableSourceOfProjectReferenceRedirect?: boolean;
|
||||||
downlevelIteration?: boolean;
|
downlevelIteration?: boolean;
|
||||||
emitBOM?: boolean;
|
emitBOM?: boolean;
|
||||||
emitDecoratorMetadata?: boolean;
|
emitDecoratorMetadata?: boolean;
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"disableSourceOfProjectReferenceRedirect": true
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue