mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:32:08 -06:00
- checker.ts: Check for wildcard before choosing error message - programDiagnostics.ts: Check for wildcard before returning early - watch.ts: Check for wildcard to choose correct message - jsTyping.ts: Use optional chaining for wildcard check - tscWatch tests: Use wildcard for tests that dynamically install types These functions should only show "add to types" messages when wildcard is NOT present. Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
999 lines
45 KiB
TypeScript
999 lines
45 KiB
TypeScript
import {
|
|
addRange,
|
|
BuilderProgram,
|
|
CancellationToken,
|
|
chainDiagnosticMessages,
|
|
CharacterCodes,
|
|
combinePaths,
|
|
CompilerHost,
|
|
CompilerOptions,
|
|
contains,
|
|
convertToRelativePath,
|
|
copyProperties,
|
|
countWhere,
|
|
createCompilerDiagnostic,
|
|
createEmitAndSemanticDiagnosticsBuilderProgram,
|
|
createGetCanonicalFileName,
|
|
createGetSourceFile,
|
|
createIncrementalCompilerHost,
|
|
createIncrementalProgram,
|
|
CreateProgram,
|
|
createWriteFileMeasuringIO,
|
|
CustomTransformers,
|
|
Debug,
|
|
Diagnostic,
|
|
DiagnosticAndArguments,
|
|
DiagnosticCategory,
|
|
DiagnosticMessage,
|
|
DiagnosticMessageChain,
|
|
DiagnosticReporter,
|
|
Diagnostics,
|
|
DirectoryStructureHost,
|
|
EmitAndSemanticDiagnosticsBuilderProgram,
|
|
EmitResult,
|
|
emptyArray,
|
|
endsWith,
|
|
ExitStatus,
|
|
ExtendedConfigCacheEntry,
|
|
Extension,
|
|
externalHelpersModuleNameText,
|
|
FileExtensionInfo,
|
|
fileExtensionIs,
|
|
FileIncludeKind,
|
|
FileIncludeReason,
|
|
FileWatcher,
|
|
filter,
|
|
find,
|
|
findIndex,
|
|
flattenDiagnosticMessageText,
|
|
forEach,
|
|
ForegroundColorEscapeSequences,
|
|
formatColorAndReset,
|
|
formatDiagnostic,
|
|
FormatDiagnosticsHost,
|
|
formatDiagnosticsWithColorAndContext,
|
|
generateDjb2Hash,
|
|
getDefaultLibFileName,
|
|
getDirectoryPath,
|
|
getEmitDeclarations,
|
|
getEmitScriptTarget,
|
|
getImpliedNodeFormatForEmitWorker,
|
|
getLineAndCharacterOfPosition,
|
|
getNameOfScriptTarget,
|
|
getNewLineCharacter,
|
|
getNormalizedAbsolutePath,
|
|
getParsedCommandLineOfConfigFile,
|
|
getPatternFromSpec,
|
|
getReferencedFileLocation,
|
|
getRegexFromPattern,
|
|
getRelativePathFromDirectory,
|
|
getWatchFactory,
|
|
HasCurrentDirectory,
|
|
isExternalOrCommonJsModule,
|
|
isLineBreak,
|
|
isReferencedFile,
|
|
isReferenceFileLocation,
|
|
isString,
|
|
last,
|
|
maxBy,
|
|
maybeBind,
|
|
memoize,
|
|
ModuleKind,
|
|
noop,
|
|
normalizePath,
|
|
packageIdToString,
|
|
ParseConfigFileHost,
|
|
ParsedCommandLine,
|
|
pathIsAbsolute,
|
|
Program,
|
|
ProgramHost,
|
|
ProjectReference,
|
|
ReportEmitErrorSummary,
|
|
ReportFileInError,
|
|
sortAndDeduplicateDiagnostics,
|
|
SortedReadonlyArray,
|
|
SourceFile,
|
|
sourceMapCommentRegExp,
|
|
sourceMapCommentRegExpDontCareLineStart,
|
|
sys,
|
|
System,
|
|
WatchCompilerHost,
|
|
WatchCompilerHostOfConfigFile,
|
|
WatchCompilerHostOfFilesAndCompilerOptions,
|
|
WatchFactory,
|
|
WatchFactoryHost,
|
|
WatchHost,
|
|
WatchLogLevel,
|
|
WatchOptions,
|
|
WatchStatusReporter,
|
|
whitespaceOrMapCommentRegExp,
|
|
WriteFileCallback,
|
|
} from "./_namespaces/ts.js";
|
|
|
|
const sysFormatDiagnosticsHost: FormatDiagnosticsHost | undefined = sys ? {
|
|
getCurrentDirectory: () => sys.getCurrentDirectory(),
|
|
getNewLine: () => sys.newLine,
|
|
getCanonicalFileName: createGetCanonicalFileName(sys.useCaseSensitiveFileNames),
|
|
} : undefined;
|
|
|
|
/**
|
|
* Create a function that reports error by writing to the system and handles the formatting of the diagnostic
|
|
*
|
|
* @internal
|
|
*/
|
|
export function createDiagnosticReporter(system: System, pretty?: boolean): DiagnosticReporter {
|
|
const host: FormatDiagnosticsHost = system === sys && sysFormatDiagnosticsHost ? sysFormatDiagnosticsHost : {
|
|
getCurrentDirectory: () => system.getCurrentDirectory(),
|
|
getNewLine: () => system.newLine,
|
|
getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames),
|
|
};
|
|
if (!pretty) {
|
|
return diagnostic => system.write(formatDiagnostic(diagnostic, host));
|
|
}
|
|
|
|
const diagnostics: Diagnostic[] = new Array(1);
|
|
return diagnostic => {
|
|
diagnostics[0] = diagnostic;
|
|
system.write(formatDiagnosticsWithColorAndContext(diagnostics, host) + host.getNewLine());
|
|
diagnostics[0] = undefined!; // TODO: GH#18217
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @returns Whether the screen was cleared.
|
|
*/
|
|
function clearScreenIfNotWatchingForFileChanges(system: System, diagnostic: Diagnostic, options: CompilerOptions): boolean {
|
|
if (
|
|
system.clearScreen &&
|
|
!options.preserveWatchOutput &&
|
|
!options.extendedDiagnostics &&
|
|
!options.diagnostics &&
|
|
contains(screenStartingMessageCodes, diagnostic.code)
|
|
) {
|
|
system.clearScreen();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const screenStartingMessageCodes: number[] = [
|
|
Diagnostics.Starting_compilation_in_watch_mode.code,
|
|
Diagnostics.File_change_detected_Starting_incremental_compilation.code,
|
|
];
|
|
|
|
function getPlainDiagnosticFollowingNewLines(diagnostic: Diagnostic, newLine: string): string {
|
|
return contains(screenStartingMessageCodes, diagnostic.code)
|
|
? newLine + newLine
|
|
: newLine;
|
|
}
|
|
|
|
/**
|
|
* Get locale specific time based on whether we are in test mode
|
|
*
|
|
* @internal
|
|
*/
|
|
export function getLocaleTimeString(system: System): string {
|
|
return !system.now ?
|
|
new Date().toLocaleTimeString() :
|
|
// On some systems / builds of Node, there's a non-breaking space between the time and AM/PM.
|
|
// This branch is solely for testing, so just switch it to a normal space for baseline stability.
|
|
// See:
|
|
// - https://github.com/nodejs/node/issues/45171
|
|
// - https://github.com/nodejs/node/issues/45753
|
|
system.now().toLocaleTimeString("en-US", { timeZone: "UTC" }).replace("\u202f", " ");
|
|
}
|
|
|
|
/**
|
|
* Create a function that reports watch status by writing to the system and handles the formatting of the diagnostic
|
|
*
|
|
* @internal
|
|
*/
|
|
export function createWatchStatusReporter(system: System, pretty?: boolean): WatchStatusReporter {
|
|
return pretty ?
|
|
(diagnostic, newLine, options) => {
|
|
clearScreenIfNotWatchingForFileChanges(system, diagnostic, options);
|
|
let output = `[${formatColorAndReset(getLocaleTimeString(system), ForegroundColorEscapeSequences.Grey)}] `;
|
|
output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${newLine + newLine}`;
|
|
system.write(output);
|
|
} :
|
|
(diagnostic, newLine, options) => {
|
|
let output = "";
|
|
|
|
if (!clearScreenIfNotWatchingForFileChanges(system, diagnostic, options)) {
|
|
output += newLine;
|
|
}
|
|
|
|
output += `${getLocaleTimeString(system)} - `;
|
|
output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${getPlainDiagnosticFollowingNewLines(diagnostic, newLine)}`;
|
|
|
|
system.write(output);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Parses config file using System interface
|
|
*
|
|
* @internal
|
|
*/
|
|
export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, extendedConfigCache: Map<string, ExtendedConfigCacheEntry> | undefined, watchOptionsToExtend: WatchOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter): ParsedCommandLine | undefined {
|
|
const host: ParseConfigFileHost = system as any;
|
|
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic);
|
|
const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host, extendedConfigCache, watchOptionsToExtend);
|
|
host.onUnRecoverableConfigFileDiagnostic = undefined!; // TODO: GH#18217
|
|
return result;
|
|
}
|
|
|
|
/** @internal */
|
|
export function getErrorCountForSummary(diagnostics: readonly Diagnostic[]): number {
|
|
return countWhere(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error);
|
|
}
|
|
|
|
/** @internal */
|
|
export function getFilesInErrorForSummary(diagnostics: readonly Diagnostic[]): (ReportFileInError | undefined)[] {
|
|
const filesInError = filter(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error)
|
|
.map(
|
|
errorDiagnostic => {
|
|
if (errorDiagnostic.file === undefined) return;
|
|
return `${errorDiagnostic.file.fileName}`;
|
|
},
|
|
);
|
|
return filesInError.map(fileName => {
|
|
if (fileName === undefined) {
|
|
return undefined;
|
|
}
|
|
|
|
const diagnosticForFileName = find(diagnostics, diagnostic => diagnostic.file !== undefined && diagnostic.file.fileName === fileName);
|
|
|
|
if (diagnosticForFileName !== undefined) {
|
|
const { line } = getLineAndCharacterOfPosition(diagnosticForFileName.file!, diagnosticForFileName.start!);
|
|
return {
|
|
fileName,
|
|
line: line + 1,
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
/** @internal */
|
|
export function getWatchErrorSummaryDiagnosticMessage(errorCount: number): DiagnosticMessage {
|
|
return errorCount === 1 ?
|
|
Diagnostics.Found_1_error_Watching_for_file_changes :
|
|
Diagnostics.Found_0_errors_Watching_for_file_changes;
|
|
}
|
|
|
|
function prettyPathForFileError(error: ReportFileInError, cwd: string) {
|
|
const line = formatColorAndReset(":" + error.line, ForegroundColorEscapeSequences.Grey);
|
|
if (pathIsAbsolute(error.fileName) && pathIsAbsolute(cwd)) {
|
|
return getRelativePathFromDirectory(cwd, error.fileName, /*ignoreCase*/ false) + line;
|
|
}
|
|
|
|
return error.fileName + line;
|
|
}
|
|
|
|
/** @internal */
|
|
export function getErrorSummaryText(
|
|
errorCount: number,
|
|
filesInError: readonly (ReportFileInError | undefined)[],
|
|
newLine: string,
|
|
host: HasCurrentDirectory,
|
|
): string {
|
|
if (errorCount === 0) return "";
|
|
const nonNilFiles = filesInError.filter(fileInError => fileInError !== undefined);
|
|
const distinctFileNamesWithLines = nonNilFiles.map(fileInError => `${fileInError.fileName}:${fileInError.line}`)
|
|
.filter((value, index, self) => self.indexOf(value) === index);
|
|
|
|
const firstFileReference = nonNilFiles[0] && prettyPathForFileError(nonNilFiles[0], host.getCurrentDirectory());
|
|
|
|
let messageAndArgs: DiagnosticAndArguments;
|
|
if (errorCount === 1) {
|
|
messageAndArgs = filesInError[0] !== undefined ? [Diagnostics.Found_1_error_in_0, firstFileReference] : [Diagnostics.Found_1_error];
|
|
}
|
|
else {
|
|
messageAndArgs = distinctFileNamesWithLines.length === 0 ? [Diagnostics.Found_0_errors, errorCount] :
|
|
distinctFileNamesWithLines.length === 1 ? [Diagnostics.Found_0_errors_in_the_same_file_starting_at_Colon_1, errorCount, firstFileReference] :
|
|
[Diagnostics.Found_0_errors_in_1_files, errorCount, distinctFileNamesWithLines.length];
|
|
}
|
|
|
|
const d = createCompilerDiagnostic(...messageAndArgs);
|
|
const suffix = distinctFileNamesWithLines.length > 1 ? createTabularErrorsDisplay(nonNilFiles, host) : "";
|
|
return `${newLine}${flattenDiagnosticMessageText(d.messageText, newLine)}${newLine}${newLine}${suffix}`;
|
|
}
|
|
|
|
function createTabularErrorsDisplay(filesInError: (ReportFileInError | undefined)[], host: HasCurrentDirectory) {
|
|
const distinctFiles = filesInError.filter((value, index, self) => index === self.findIndex(file => file?.fileName === value?.fileName));
|
|
if (distinctFiles.length === 0) return "";
|
|
|
|
const numberLength = (num: number) => Math.log(num) * Math.LOG10E + 1;
|
|
const fileToErrorCount = distinctFiles.map(file => ([file, countWhere(filesInError, fileInError => fileInError!.fileName === file!.fileName)] as const));
|
|
const maxErrors = maxBy(fileToErrorCount, 0, value => value[1]);
|
|
|
|
const headerRow = Diagnostics.Errors_Files.message;
|
|
const leftColumnHeadingLength = headerRow.split(" ")[0].length;
|
|
const leftPaddingGoal = Math.max(leftColumnHeadingLength, numberLength(maxErrors));
|
|
const headerPadding = Math.max(numberLength(maxErrors) - leftColumnHeadingLength, 0);
|
|
|
|
let tabularData = "";
|
|
tabularData += " ".repeat(headerPadding) + headerRow + "\n";
|
|
fileToErrorCount.forEach(row => {
|
|
const [file, errorCount] = row;
|
|
const errorCountDigitsLength = Math.log(errorCount) * Math.LOG10E + 1 | 0;
|
|
const leftPadding = errorCountDigitsLength < leftPaddingGoal ?
|
|
" ".repeat(leftPaddingGoal - errorCountDigitsLength)
|
|
: "";
|
|
|
|
const fileRef = prettyPathForFileError(file!, host.getCurrentDirectory());
|
|
tabularData += `${leftPadding}${errorCount} ${fileRef}\n`;
|
|
});
|
|
|
|
return tabularData;
|
|
}
|
|
|
|
/** @internal */
|
|
export function isBuilderProgram<T extends BuilderProgram>(program: Program | BuilderProgram): program is T {
|
|
return !!(program as T).state;
|
|
}
|
|
|
|
function listFiles<T extends BuilderProgram>(program: Program | T, write: (s: string) => void) {
|
|
const options = program.getCompilerOptions();
|
|
if (options.explainFiles) {
|
|
explainFiles(isBuilderProgram(program) ? program.getProgram() : program, write);
|
|
}
|
|
else if (options.listFiles || options.listFilesOnly) {
|
|
forEach(program.getSourceFiles(), file => {
|
|
write(file.fileName);
|
|
});
|
|
}
|
|
}
|
|
|
|
/** @internal */
|
|
export function explainFiles(program: Program, write: (s: string) => void): void {
|
|
const reasons = program.getFileIncludeReasons();
|
|
const relativeFileName = (fileName: string) => convertToRelativePath(fileName, program.getCurrentDirectory(), program.getCanonicalFileName);
|
|
for (const file of program.getSourceFiles()) {
|
|
write(`${toFileName(file, relativeFileName)}`);
|
|
reasons.get(file.path)?.forEach(reason => write(` ${fileIncludeReasonToDiagnostics(program, reason, relativeFileName).messageText}`));
|
|
explainIfFileIsRedirectAndImpliedFormat(file, program.getCompilerOptionsForFile(file), relativeFileName)?.forEach(d => write(` ${d.messageText}`));
|
|
}
|
|
}
|
|
|
|
/** @internal */
|
|
export function explainIfFileIsRedirectAndImpliedFormat(
|
|
file: SourceFile,
|
|
options: CompilerOptions,
|
|
fileNameConvertor?: (fileName: string) => string,
|
|
): DiagnosticMessageChain[] | undefined {
|
|
let result: DiagnosticMessageChain[] | undefined;
|
|
if (file.path !== file.resolvedPath) {
|
|
(result ??= []).push(chainDiagnosticMessages(
|
|
/*details*/ undefined,
|
|
Diagnostics.File_is_output_of_project_reference_source_0,
|
|
toFileName(file.originalFileName, fileNameConvertor),
|
|
));
|
|
}
|
|
if (file.redirectInfo) {
|
|
(result ??= []).push(chainDiagnosticMessages(
|
|
/*details*/ undefined,
|
|
Diagnostics.File_redirects_to_file_0,
|
|
toFileName(file.redirectInfo.redirectTarget, fileNameConvertor),
|
|
));
|
|
}
|
|
if (isExternalOrCommonJsModule(file)) {
|
|
switch (getImpliedNodeFormatForEmitWorker(file, options)) {
|
|
case ModuleKind.ESNext:
|
|
if (file.packageJsonScope) {
|
|
(result ??= []).push(chainDiagnosticMessages(
|
|
/*details*/ undefined,
|
|
Diagnostics.File_is_ECMAScript_module_because_0_has_field_type_with_value_module,
|
|
toFileName(last(file.packageJsonLocations!), fileNameConvertor),
|
|
));
|
|
}
|
|
break;
|
|
case ModuleKind.CommonJS:
|
|
if (file.packageJsonScope) {
|
|
(result ??= []).push(chainDiagnosticMessages(
|
|
/*details*/ undefined,
|
|
file.packageJsonScope.contents.packageJsonContent.type ?
|
|
Diagnostics.File_is_CommonJS_module_because_0_has_field_type_whose_value_is_not_module :
|
|
Diagnostics.File_is_CommonJS_module_because_0_does_not_have_field_type,
|
|
toFileName(last(file.packageJsonLocations!), fileNameConvertor),
|
|
));
|
|
}
|
|
else if (file.packageJsonLocations?.length) {
|
|
(result ??= []).push(chainDiagnosticMessages(
|
|
/*details*/ undefined,
|
|
Diagnostics.File_is_CommonJS_module_because_package_json_was_not_found,
|
|
));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/** @internal */
|
|
export function getMatchedFileSpec(program: Program, fileName: string): string | undefined {
|
|
const configFile = program.getCompilerOptions().configFile;
|
|
if (!configFile?.configFileSpecs?.validatedFilesSpec) return undefined;
|
|
|
|
const filePath = program.getCanonicalFileName(fileName);
|
|
const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory()));
|
|
const index = findIndex(configFile.configFileSpecs.validatedFilesSpec, fileSpec => program.getCanonicalFileName(getNormalizedAbsolutePath(fileSpec, basePath)) === filePath);
|
|
return index !== -1 ? configFile.configFileSpecs.validatedFilesSpecBeforeSubstitution![index] : undefined;
|
|
}
|
|
|
|
/** @internal */
|
|
export function getMatchedIncludeSpec(program: Program, fileName: string): string | true | undefined {
|
|
const configFile = program.getCompilerOptions().configFile;
|
|
if (!configFile?.configFileSpecs?.validatedIncludeSpecs) return undefined;
|
|
|
|
// Return true if its default include spec
|
|
if (configFile.configFileSpecs.isDefaultIncludeSpec) return true;
|
|
|
|
const isJsonFile = fileExtensionIs(fileName, Extension.Json);
|
|
const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory()));
|
|
const useCaseSensitiveFileNames = program.useCaseSensitiveFileNames();
|
|
const index = findIndex(configFile?.configFileSpecs?.validatedIncludeSpecs, includeSpec => {
|
|
if (isJsonFile && !endsWith(includeSpec, Extension.Json)) return false;
|
|
const pattern = getPatternFromSpec(includeSpec, basePath, "files");
|
|
return !!pattern && getRegexFromPattern(`(?:${pattern})$`, useCaseSensitiveFileNames).test(fileName);
|
|
});
|
|
return index !== -1 ? configFile.configFileSpecs.validatedIncludeSpecsBeforeSubstitution![index] : undefined;
|
|
}
|
|
|
|
/** @internal */
|
|
export function fileIncludeReasonToDiagnostics(program: Program, reason: FileIncludeReason, fileNameConvertor?: (fileName: string) => string): DiagnosticMessageChain {
|
|
const options = program.getCompilerOptions();
|
|
if (isReferencedFile(reason)) {
|
|
const referenceLocation = getReferencedFileLocation(program, reason);
|
|
const referenceText = isReferenceFileLocation(referenceLocation) ? referenceLocation.file.text.substring(referenceLocation.pos, referenceLocation.end) : `"${referenceLocation.text}"`;
|
|
let message: DiagnosticMessage;
|
|
Debug.assert(isReferenceFileLocation(referenceLocation) || reason.kind === FileIncludeKind.Import, "Only synthetic references are imports");
|
|
switch (reason.kind) {
|
|
case FileIncludeKind.Import:
|
|
if (isReferenceFileLocation(referenceLocation)) {
|
|
message = referenceLocation.packageId ?
|
|
Diagnostics.Imported_via_0_from_file_1_with_packageId_2 :
|
|
Diagnostics.Imported_via_0_from_file_1;
|
|
}
|
|
else if (referenceLocation.text === externalHelpersModuleNameText) {
|
|
message = referenceLocation.packageId ?
|
|
Diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_importHelpers_as_specified_in_compilerOptions :
|
|
Diagnostics.Imported_via_0_from_file_1_to_import_importHelpers_as_specified_in_compilerOptions;
|
|
}
|
|
else {
|
|
message = referenceLocation.packageId ?
|
|
Diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_jsx_and_jsxs_factory_functions :
|
|
Diagnostics.Imported_via_0_from_file_1_to_import_jsx_and_jsxs_factory_functions;
|
|
}
|
|
break;
|
|
case FileIncludeKind.ReferenceFile:
|
|
Debug.assert(!referenceLocation.packageId);
|
|
message = Diagnostics.Referenced_via_0_from_file_1;
|
|
break;
|
|
case FileIncludeKind.TypeReferenceDirective:
|
|
message = referenceLocation.packageId ?
|
|
Diagnostics.Type_library_referenced_via_0_from_file_1_with_packageId_2 :
|
|
Diagnostics.Type_library_referenced_via_0_from_file_1;
|
|
break;
|
|
case FileIncludeKind.LibReferenceDirective:
|
|
Debug.assert(!referenceLocation.packageId);
|
|
message = Diagnostics.Library_referenced_via_0_from_file_1;
|
|
break;
|
|
default:
|
|
Debug.assertNever(reason);
|
|
}
|
|
return chainDiagnosticMessages(
|
|
/*details*/ undefined,
|
|
message,
|
|
referenceText,
|
|
toFileName(referenceLocation.file, fileNameConvertor),
|
|
(referenceLocation.packageId && packageIdToString(referenceLocation.packageId))!,
|
|
);
|
|
}
|
|
switch (reason.kind) {
|
|
case FileIncludeKind.RootFile:
|
|
if (!options.configFile?.configFileSpecs) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Root_file_specified_for_compilation);
|
|
const fileName = getNormalizedAbsolutePath(program.getRootFileNames()[reason.index], program.getCurrentDirectory());
|
|
const matchedByFiles = getMatchedFileSpec(program, fileName);
|
|
if (matchedByFiles) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Part_of_files_list_in_tsconfig_json);
|
|
const matchedByInclude = getMatchedIncludeSpec(program, fileName);
|
|
return isString(matchedByInclude) ?
|
|
chainDiagnosticMessages(
|
|
/*details*/ undefined,
|
|
Diagnostics.Matched_by_include_pattern_0_in_1,
|
|
matchedByInclude,
|
|
toFileName(options.configFile, fileNameConvertor),
|
|
) :
|
|
// Could be additional files specified as roots or matched by default include
|
|
chainDiagnosticMessages(
|
|
/*details*/ undefined,
|
|
matchedByInclude ?
|
|
Diagnostics.Matched_by_default_include_pattern_Asterisk_Asterisk_Slash_Asterisk :
|
|
Diagnostics.Root_file_specified_for_compilation,
|
|
);
|
|
case FileIncludeKind.SourceFromProjectReference:
|
|
case FileIncludeKind.OutputFromProjectReference:
|
|
const isOutput = reason.kind === FileIncludeKind.OutputFromProjectReference;
|
|
const referencedResolvedRef = Debug.checkDefined(program.getResolvedProjectReferences()?.[reason.index]);
|
|
return chainDiagnosticMessages(
|
|
/*details*/ undefined,
|
|
options.outFile ?
|
|
isOutput ?
|
|
Diagnostics.Output_from_referenced_project_0_included_because_1_specified :
|
|
Diagnostics.Source_from_referenced_project_0_included_because_1_specified :
|
|
isOutput ?
|
|
Diagnostics.Output_from_referenced_project_0_included_because_module_is_specified_as_none :
|
|
Diagnostics.Source_from_referenced_project_0_included_because_module_is_specified_as_none,
|
|
toFileName(referencedResolvedRef.sourceFile.fileName, fileNameConvertor),
|
|
options.outFile ? "--outFile" : "--out",
|
|
);
|
|
case FileIncludeKind.AutomaticTypeDirectiveFile: {
|
|
const messageAndArgs: DiagnosticAndArguments = options.types?.includes("*") ?
|
|
reason.packageId ?
|
|
[Diagnostics.Entry_point_for_implicit_type_library_0_with_packageId_1, reason.typeReference, packageIdToString(reason.packageId)] :
|
|
[Diagnostics.Entry_point_for_implicit_type_library_0, reason.typeReference] :
|
|
reason.packageId ?
|
|
[Diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions_with_packageId_1, reason.typeReference, packageIdToString(reason.packageId)] :
|
|
[Diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions, reason.typeReference];
|
|
|
|
return chainDiagnosticMessages(/*details*/ undefined, ...messageAndArgs);
|
|
}
|
|
case FileIncludeKind.LibFile: {
|
|
if (reason.index !== undefined) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Library_0_specified_in_compilerOptions, options.lib![reason.index]);
|
|
const target = getNameOfScriptTarget(getEmitScriptTarget(options));
|
|
const messageAndArgs: DiagnosticAndArguments = target ? [Diagnostics.Default_library_for_target_0, target] : [Diagnostics.Default_library];
|
|
return chainDiagnosticMessages(/*details*/ undefined, ...messageAndArgs);
|
|
}
|
|
default:
|
|
Debug.assertNever(reason);
|
|
}
|
|
}
|
|
|
|
function toFileName(file: SourceFile | string, fileNameConvertor?: (fileName: string) => string) {
|
|
const fileName = isString(file) ? file : file.fileName;
|
|
return fileNameConvertor ? fileNameConvertor(fileName) : fileName;
|
|
}
|
|
|
|
/**
|
|
* Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options
|
|
*
|
|
* @internal
|
|
*/
|
|
export function emitFilesAndReportErrors<T extends BuilderProgram>(
|
|
program: Program | T,
|
|
reportDiagnostic: DiagnosticReporter,
|
|
write?: (s: string) => void,
|
|
reportSummary?: ReportEmitErrorSummary,
|
|
writeFile?: WriteFileCallback,
|
|
cancellationToken?: CancellationToken,
|
|
emitOnlyDtsFiles?: boolean,
|
|
customTransformers?: CustomTransformers,
|
|
): {
|
|
emitResult: EmitResult;
|
|
diagnostics: SortedReadonlyArray<Diagnostic>;
|
|
} {
|
|
const options = program.getCompilerOptions();
|
|
|
|
// First get and report any syntactic errors.
|
|
const allDiagnostics = program.getConfigFileParsingDiagnostics().slice();
|
|
const configFileParsingDiagnosticsLength = allDiagnostics.length;
|
|
addRange(allDiagnostics, program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
|
|
|
|
// If we didn't have any syntactic errors, then also try getting the global and
|
|
// semantic errors.
|
|
if (allDiagnostics.length === configFileParsingDiagnosticsLength) {
|
|
addRange(allDiagnostics, program.getOptionsDiagnostics(cancellationToken));
|
|
|
|
if (!options.listFilesOnly) {
|
|
addRange(allDiagnostics, program.getGlobalDiagnostics(cancellationToken));
|
|
|
|
if (allDiagnostics.length === configFileParsingDiagnosticsLength) {
|
|
addRange(allDiagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken));
|
|
}
|
|
|
|
if (
|
|
options.noEmit &&
|
|
getEmitDeclarations(options) &&
|
|
allDiagnostics.length === configFileParsingDiagnosticsLength
|
|
) {
|
|
addRange(allDiagnostics, program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Emit and report any errors we ran into.
|
|
const emitResult = options.listFilesOnly
|
|
? { emitSkipped: true, diagnostics: emptyArray }
|
|
: program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
|
addRange(allDiagnostics, emitResult.diagnostics);
|
|
|
|
const diagnostics = sortAndDeduplicateDiagnostics(allDiagnostics);
|
|
diagnostics.forEach(reportDiagnostic);
|
|
if (write) {
|
|
const currentDir = program.getCurrentDirectory();
|
|
forEach(emitResult.emittedFiles, file => {
|
|
const filepath = getNormalizedAbsolutePath(file, currentDir);
|
|
write(`TSFILE: ${filepath}`);
|
|
});
|
|
listFiles(program, write);
|
|
}
|
|
|
|
if (reportSummary) {
|
|
reportSummary(getErrorCountForSummary(diagnostics), getFilesInErrorForSummary(diagnostics));
|
|
}
|
|
|
|
return {
|
|
emitResult,
|
|
diagnostics,
|
|
};
|
|
}
|
|
|
|
/** @internal */
|
|
export function emitFilesAndReportErrorsAndGetExitStatus<T extends BuilderProgram>(
|
|
program: Program | T,
|
|
reportDiagnostic: DiagnosticReporter,
|
|
write?: (s: string) => void,
|
|
reportSummary?: ReportEmitErrorSummary,
|
|
writeFile?: WriteFileCallback,
|
|
cancellationToken?: CancellationToken,
|
|
emitOnlyDtsFiles?: boolean,
|
|
customTransformers?: CustomTransformers,
|
|
): ExitStatus {
|
|
const { emitResult, diagnostics } = emitFilesAndReportErrors(
|
|
program,
|
|
reportDiagnostic,
|
|
write,
|
|
reportSummary,
|
|
writeFile,
|
|
cancellationToken,
|
|
emitOnlyDtsFiles,
|
|
customTransformers,
|
|
);
|
|
|
|
if (emitResult.emitSkipped && diagnostics.length > 0) {
|
|
// If the emitter didn't emit anything, then pass that value along.
|
|
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
|
|
}
|
|
else if (diagnostics.length > 0) {
|
|
// The emitter emitted something, inform the caller if that happened in the presence
|
|
// of diagnostics or not.
|
|
return ExitStatus.DiagnosticsPresent_OutputsGenerated;
|
|
}
|
|
return ExitStatus.Success;
|
|
}
|
|
|
|
/** @internal */
|
|
export const noopFileWatcher: FileWatcher = { close: noop };
|
|
/** @internal */
|
|
export const returnNoopFileWatcher = (): FileWatcher => noopFileWatcher;
|
|
|
|
/** @internal */
|
|
export function createWatchHost(system: System = sys, reportWatchStatus?: WatchStatusReporter): WatchHost {
|
|
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
|
|
return {
|
|
onWatchStatusChange,
|
|
watchFile: maybeBind(system, system.watchFile) || returnNoopFileWatcher,
|
|
watchDirectory: maybeBind(system, system.watchDirectory) || returnNoopFileWatcher,
|
|
setTimeout: maybeBind(system, system.setTimeout) || noop,
|
|
clearTimeout: maybeBind(system, system.clearTimeout) || noop,
|
|
preferNonRecursiveWatch: system.preferNonRecursiveWatch,
|
|
};
|
|
}
|
|
|
|
/** @internal */
|
|
export type WatchType = WatchTypeRegistry[keyof WatchTypeRegistry];
|
|
/** @internal */
|
|
export const WatchType: WatchTypeRegistry = {
|
|
ConfigFile: "Config file",
|
|
ExtendedConfigFile: "Extended config file",
|
|
SourceFile: "Source file",
|
|
MissingFile: "Missing file",
|
|
WildcardDirectory: "Wild card directory",
|
|
FailedLookupLocations: "Failed Lookup Locations",
|
|
AffectingFileLocation: "File location affecting resolution",
|
|
TypeRoots: "Type roots",
|
|
ConfigFileOfReferencedProject: "Config file of referened project",
|
|
ExtendedConfigOfReferencedProject: "Extended config file of referenced project",
|
|
WildcardDirectoryOfReferencedProject: "Wild card directory of referenced project",
|
|
PackageJson: "package.json file",
|
|
ClosedScriptInfo: "Closed Script info",
|
|
ConfigFileForInferredRoot: "Config file for the inferred project root",
|
|
NodeModules: "node_modules for closed script infos and package.jsons affecting module specifier cache",
|
|
MissingSourceMapFile: "Missing source map file",
|
|
NoopConfigFileForInferredRoot: "Noop Config file for the inferred project root",
|
|
MissingGeneratedFile: "Missing generated file",
|
|
NodeModulesForModuleSpecifierCache: "node_modules for module specifier cache invalidation",
|
|
TypingInstallerLocationFile: "File location for typing installer",
|
|
TypingInstallerLocationDirectory: "Directory location for typing installer",
|
|
};
|
|
|
|
/** @internal */
|
|
export interface WatchTypeRegistry {
|
|
ConfigFile: "Config file";
|
|
ExtendedConfigFile: "Extended config file";
|
|
SourceFile: "Source file";
|
|
MissingFile: "Missing file";
|
|
WildcardDirectory: "Wild card directory";
|
|
FailedLookupLocations: "Failed Lookup Locations";
|
|
AffectingFileLocation: "File location affecting resolution";
|
|
TypeRoots: "Type roots";
|
|
ConfigFileOfReferencedProject: "Config file of referened project";
|
|
ExtendedConfigOfReferencedProject: "Extended config file of referenced project";
|
|
WildcardDirectoryOfReferencedProject: "Wild card directory of referenced project";
|
|
PackageJson: "package.json file";
|
|
|
|
// Additional tsserver specific watch information
|
|
ClosedScriptInfo: "Closed Script info";
|
|
ConfigFileForInferredRoot: "Config file for the inferred project root";
|
|
NodeModules: "node_modules for closed script infos and package.jsons affecting module specifier cache";
|
|
MissingSourceMapFile: "Missing source map file";
|
|
NoopConfigFileForInferredRoot: "Noop Config file for the inferred project root";
|
|
MissingGeneratedFile: "Missing generated file";
|
|
NodeModulesForModuleSpecifierCache: "node_modules for module specifier cache invalidation";
|
|
TypingInstallerLocationFile: "File location for typing installer";
|
|
TypingInstallerLocationDirectory: "Directory location for typing installer";
|
|
}
|
|
|
|
/** @internal */
|
|
export interface WatchFactoryWithLog<X, Y = undefined> extends WatchFactory<X, Y> {
|
|
writeLog: (s: string) => void;
|
|
}
|
|
|
|
/** @internal */
|
|
export function createWatchFactory<Y = undefined>(host: WatchFactoryHost & { trace?(s: string): void; }, options: { extendedDiagnostics?: boolean; diagnostics?: boolean; }): WatchFactoryWithLog<WatchType, Y> {
|
|
const watchLogLevel = host.trace ? options.extendedDiagnostics ? WatchLogLevel.Verbose : options.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None : WatchLogLevel.None;
|
|
const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => host.trace!(s)) : noop;
|
|
const result = getWatchFactory<WatchType, Y>(host, watchLogLevel, writeLog) as WatchFactoryWithLog<WatchType, Y>;
|
|
result.writeLog = writeLog;
|
|
return result;
|
|
}
|
|
|
|
/** @internal */
|
|
export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCompilerOptions: () => CompilerOptions, directoryStructureHost: DirectoryStructureHost = host): CompilerHost {
|
|
const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames();
|
|
const compilerHost: CompilerHost = {
|
|
getSourceFile: createGetSourceFile(
|
|
(fileName, encoding) => !encoding ? compilerHost.readFile(fileName) : host.readFile(fileName, encoding),
|
|
/*setParentNodes*/ undefined,
|
|
),
|
|
getDefaultLibLocation: maybeBind(host, host.getDefaultLibLocation),
|
|
getDefaultLibFileName: options => host.getDefaultLibFileName(options),
|
|
writeFile: createWriteFileMeasuringIO(
|
|
(path, data, writeByteOrderMark) => host.writeFile!(path, data, writeByteOrderMark),
|
|
path => host.createDirectory!(path),
|
|
path => host.directoryExists!(path),
|
|
),
|
|
getCurrentDirectory: memoize(() => host.getCurrentDirectory()),
|
|
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
|
getCanonicalFileName: createGetCanonicalFileName(useCaseSensitiveFileNames),
|
|
getNewLine: () => getNewLineCharacter(getCompilerOptions()),
|
|
fileExists: f => host.fileExists(f),
|
|
readFile: f => host.readFile(f),
|
|
trace: maybeBind(host, host.trace),
|
|
directoryExists: maybeBind(directoryStructureHost, directoryStructureHost.directoryExists),
|
|
getDirectories: maybeBind(directoryStructureHost, directoryStructureHost.getDirectories),
|
|
realpath: maybeBind(host, host.realpath),
|
|
getEnvironmentVariable: maybeBind(host, host.getEnvironmentVariable) || (() => ""),
|
|
createHash: maybeBind(host, host.createHash),
|
|
readDirectory: maybeBind(host, host.readDirectory),
|
|
storeSignatureInfo: host.storeSignatureInfo,
|
|
jsDocParsingMode: host.jsDocParsingMode,
|
|
};
|
|
return compilerHost;
|
|
}
|
|
|
|
/** @internal */
|
|
export function getSourceFileVersionAsHashFromText(host: Pick<CompilerHost, "createHash">, text: string): string {
|
|
// If text can contain the sourceMapUrl ignore sourceMapUrl for calcualting hash
|
|
if (text.match(sourceMapCommentRegExpDontCareLineStart)) {
|
|
let lineEnd = text.length;
|
|
let lineStart = lineEnd;
|
|
for (let pos = lineEnd - 1; pos >= 0; pos--) {
|
|
const ch = text.charCodeAt(pos);
|
|
switch (ch) {
|
|
case CharacterCodes.lineFeed:
|
|
if (pos && text.charCodeAt(pos - 1) === CharacterCodes.carriageReturn) {
|
|
pos--;
|
|
}
|
|
// falls through
|
|
case CharacterCodes.carriageReturn:
|
|
break;
|
|
default:
|
|
if (ch < CharacterCodes.maxAsciiCharacter || !isLineBreak(ch)) {
|
|
lineStart = pos;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
// This is start of the line
|
|
const line = text.substring(lineStart, lineEnd);
|
|
if (line.match(sourceMapCommentRegExp)) {
|
|
text = text.substring(0, lineStart);
|
|
break;
|
|
}
|
|
// If we see a non-whitespace/map comment-like line, break, to avoid scanning up the entire file
|
|
else if (!line.match(whitespaceOrMapCommentRegExp)) {
|
|
break;
|
|
}
|
|
lineEnd = lineStart;
|
|
}
|
|
}
|
|
return (host.createHash || generateDjb2Hash)(text);
|
|
}
|
|
|
|
/** @internal */
|
|
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost): void {
|
|
const originalGetSourceFile = compilerHost.getSourceFile;
|
|
compilerHost.getSourceFile = (...args) => {
|
|
const result = originalGetSourceFile.call(compilerHost, ...args);
|
|
if (result) {
|
|
result.version = getSourceFileVersionAsHashFromText(compilerHost, result.text);
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates the watch compiler host that can be extended with config file or root file names and options host
|
|
*
|
|
* @internal
|
|
*/
|
|
export function createProgramHost<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(system: System, createProgram: CreateProgram<T> | undefined): ProgramHost<T> {
|
|
const getDefaultLibLocation = memoize(() => getDirectoryPath(normalizePath(system.getExecutingFilePath())));
|
|
return {
|
|
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
|
|
getNewLine: () => system.newLine,
|
|
getCurrentDirectory: memoize(() => system.getCurrentDirectory()),
|
|
getDefaultLibLocation,
|
|
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
|
|
fileExists: path => system.fileExists(path),
|
|
readFile: (path, encoding) => system.readFile(path, encoding),
|
|
directoryExists: path => system.directoryExists(path),
|
|
getDirectories: path => system.getDirectories(path),
|
|
readDirectory: (path, extensions, exclude, include, depth) => system.readDirectory(path, extensions, exclude, include, depth),
|
|
realpath: maybeBind(system, system.realpath),
|
|
getEnvironmentVariable: maybeBind(system, system.getEnvironmentVariable),
|
|
trace: s => system.write(s + system.newLine),
|
|
createDirectory: path => system.createDirectory(path),
|
|
writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark),
|
|
createHash: maybeBind(system, system.createHash),
|
|
createProgram: createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>,
|
|
storeSignatureInfo: system.storeSignatureInfo,
|
|
now: maybeBind(system, system.now),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates the watch compiler host that can be extended with config file or root file names and options host
|
|
*/
|
|
function createWatchCompilerHost<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(system = sys, createProgram: CreateProgram<T> | undefined, reportDiagnostic: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHost<T> {
|
|
const write = (s: string) => system.write(s + system.newLine);
|
|
const result = createProgramHost(system, createProgram) as WatchCompilerHost<T>;
|
|
copyProperties(result, createWatchHost(system, reportWatchStatus));
|
|
result.afterProgramCreate = builderProgram => {
|
|
const compilerOptions = builderProgram.getCompilerOptions();
|
|
const newLine = getNewLineCharacter(compilerOptions);
|
|
|
|
emitFilesAndReportErrors(
|
|
builderProgram,
|
|
reportDiagnostic,
|
|
write,
|
|
errorCount =>
|
|
result.onWatchStatusChange!(
|
|
createCompilerDiagnostic(getWatchErrorSummaryDiagnosticMessage(errorCount), errorCount),
|
|
newLine,
|
|
compilerOptions,
|
|
errorCount,
|
|
),
|
|
);
|
|
};
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Report error and exit
|
|
*/
|
|
function reportUnrecoverableDiagnostic(system: System, reportDiagnostic: DiagnosticReporter, diagnostic: Diagnostic) {
|
|
reportDiagnostic(diagnostic);
|
|
system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
|
}
|
|
|
|
/** @internal */
|
|
export interface CreateWatchCompilerHostInput<T extends BuilderProgram> {
|
|
system: System;
|
|
createProgram?: CreateProgram<T>;
|
|
reportDiagnostic?: DiagnosticReporter;
|
|
reportWatchStatus?: WatchStatusReporter;
|
|
}
|
|
|
|
/** @internal */
|
|
export interface CreateWatchCompilerHostOfConfigFileInput<T extends BuilderProgram> extends CreateWatchCompilerHostInput<T> {
|
|
configFileName: string;
|
|
optionsToExtend?: CompilerOptions;
|
|
watchOptionsToExtend?: WatchOptions;
|
|
extraFileExtensions?: readonly FileExtensionInfo[];
|
|
}
|
|
/**
|
|
* Creates the watch compiler host from system for config file in watch mode
|
|
*
|
|
* @internal
|
|
*/
|
|
export function createWatchCompilerHostOfConfigFile<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({
|
|
configFileName,
|
|
optionsToExtend,
|
|
watchOptionsToExtend,
|
|
extraFileExtensions,
|
|
system,
|
|
createProgram,
|
|
reportDiagnostic,
|
|
reportWatchStatus,
|
|
}: CreateWatchCompilerHostOfConfigFileInput<T>): WatchCompilerHostOfConfigFile<T> {
|
|
const diagnosticReporter = reportDiagnostic || createDiagnosticReporter(system);
|
|
const host = createWatchCompilerHost(system, createProgram, diagnosticReporter, reportWatchStatus) as WatchCompilerHostOfConfigFile<T>;
|
|
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, diagnosticReporter, diagnostic);
|
|
host.configFileName = configFileName;
|
|
host.optionsToExtend = optionsToExtend;
|
|
host.watchOptionsToExtend = watchOptionsToExtend;
|
|
host.extraFileExtensions = extraFileExtensions;
|
|
return host;
|
|
}
|
|
|
|
/** @internal */
|
|
export interface CreateWatchCompilerHostOfFilesAndCompilerOptionsInput<T extends BuilderProgram> extends CreateWatchCompilerHostInput<T> {
|
|
rootFiles: string[];
|
|
options: CompilerOptions;
|
|
watchOptions: WatchOptions | undefined;
|
|
projectReferences?: readonly ProjectReference[];
|
|
}
|
|
/**
|
|
* Creates the watch compiler host from system for compiling root files and options in watch mode
|
|
*
|
|
* @internal
|
|
*/
|
|
export function createWatchCompilerHostOfFilesAndCompilerOptions<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({
|
|
rootFiles,
|
|
options,
|
|
watchOptions,
|
|
projectReferences,
|
|
system,
|
|
createProgram,
|
|
reportDiagnostic,
|
|
reportWatchStatus,
|
|
}: CreateWatchCompilerHostOfFilesAndCompilerOptionsInput<T>): WatchCompilerHostOfFilesAndCompilerOptions<T> {
|
|
const host = createWatchCompilerHost(system, createProgram, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions<T>;
|
|
host.rootFiles = rootFiles;
|
|
host.options = options;
|
|
host.watchOptions = watchOptions;
|
|
host.projectReferences = projectReferences;
|
|
return host;
|
|
}
|
|
|
|
/** @internal */
|
|
export interface IncrementalCompilationOptions {
|
|
rootNames: readonly string[];
|
|
options: CompilerOptions;
|
|
configFileParsingDiagnostics?: readonly Diagnostic[];
|
|
projectReferences?: readonly ProjectReference[];
|
|
host?: CompilerHost;
|
|
reportDiagnostic?: DiagnosticReporter;
|
|
reportErrorSummary?: ReportEmitErrorSummary;
|
|
afterProgramEmitAndDiagnostics?(program: EmitAndSemanticDiagnosticsBuilderProgram): void;
|
|
system?: System;
|
|
}
|
|
|
|
/** @internal */
|
|
export function performIncrementalCompilation(input: IncrementalCompilationOptions): ExitStatus {
|
|
const system = input.system || sys;
|
|
const host = input.host || (input.host = createIncrementalCompilerHost(input.options, system));
|
|
const builderProgram = createIncrementalProgram(input);
|
|
const exitStatus = emitFilesAndReportErrorsAndGetExitStatus(
|
|
builderProgram,
|
|
input.reportDiagnostic || createDiagnosticReporter(system),
|
|
s => host.trace && host.trace(s),
|
|
input.reportErrorSummary || input.options.pretty ? (errorCount, filesInError) => system.write(getErrorSummaryText(errorCount, filesInError, system.newLine, host)) : undefined,
|
|
);
|
|
if (input.afterProgramEmitAndDiagnostics) input.afterProgramEmitAndDiagnostics(builderProgram);
|
|
return exitStatus;
|
|
}
|