Merge pull request #22613 from Microsoft/configFileErrors

Make config file parsing errors available through program and expose API
This commit is contained in:
Sheetal Nandi
2018-03-29 13:08:03 -07:00
committed by GitHub
9 changed files with 180 additions and 121 deletions
+35 -18
View File
@@ -218,29 +218,40 @@ namespace ts {
newProgram: Program;
host: BuilderProgramHost;
oldProgram: BuilderProgram | undefined;
configFileParsingDiagnostics: ReadonlyArray<Diagnostic>;
}
export function getBuilderCreationParameters(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | BuilderProgram, oldProgram?: BuilderProgram): BuilderCreationParameters {
export function getBuilderCreationParameters(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: BuilderProgram | CompilerHost, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderCreationParameters {
let host: BuilderProgramHost;
let newProgram: Program;
if (isArray(newProgramOrRootNames)) {
newProgram = createProgram(newProgramOrRootNames, hostOrOptions as CompilerOptions, oldProgramOrHost as CompilerHost, oldProgram && oldProgram.getProgram());
let oldProgram: BuilderProgram;
if (newProgramOrRootNames === undefined) {
Debug.assert(hostOrOptions === undefined);
host = oldProgramOrHost as CompilerHost;
oldProgram = configFileParsingDiagnosticsOrOldProgram as BuilderProgram;
Debug.assert(!!oldProgram);
newProgram = oldProgram.getProgram();
}
else if (isArray(newProgramOrRootNames)) {
oldProgram = configFileParsingDiagnosticsOrOldProgram as BuilderProgram;
newProgram = createProgram(newProgramOrRootNames, hostOrOptions as CompilerOptions, oldProgramOrHost as CompilerHost, oldProgram && oldProgram.getProgram(), configFileParsingDiagnostics);
host = oldProgramOrHost as CompilerHost;
}
else {
newProgram = newProgramOrRootNames;
host = hostOrOptions as BuilderProgramHost;
oldProgram = oldProgramOrHost as BuilderProgram;
configFileParsingDiagnostics = configFileParsingDiagnosticsOrOldProgram as ReadonlyArray<Diagnostic>;
}
return { host, newProgram, oldProgram };
return { host, newProgram, oldProgram, configFileParsingDiagnostics: configFileParsingDiagnostics || emptyArray };
}
export function createBuilderProgram(kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram, builderCreationParameters: BuilderCreationParameters): SemanticDiagnosticsBuilderProgram;
export function createBuilderProgram(kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, builderCreationParameters: BuilderCreationParameters): EmitAndSemanticDiagnosticsBuilderProgram;
export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, host, oldProgram }: BuilderCreationParameters) {
export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, host, oldProgram, configFileParsingDiagnostics }: BuilderCreationParameters) {
// Return same program if underlying program doesnt change
let oldState = oldProgram && oldProgram.getState();
if (oldState && newProgram === oldState.program) {
if (oldState && newProgram === oldState.program && configFileParsingDiagnostics !== newProgram.getConfigFileParsingDiagnostics()) {
newProgram = undefined;
oldState = undefined;
return oldProgram;
@@ -269,6 +280,7 @@ namespace ts {
getSourceFiles: () => state.program.getSourceFiles(),
getOptionsDiagnostics: cancellationToken => state.program.getOptionsDiagnostics(cancellationToken),
getGlobalDiagnostics: cancellationToken => state.program.getGlobalDiagnostics(cancellationToken),
getConfigFileParsingDiagnostics: () => configFileParsingDiagnostics || state.program.getConfigFileParsingDiagnostics(),
getSyntacticDiagnostics: (sourceFile, cancellationToken) => state.program.getSyntacticDiagnostics(sourceFile, cancellationToken),
getSemanticDiagnostics,
emit,
@@ -471,6 +483,10 @@ namespace ts {
* Get the diagnostics that dont belong to any file
*/
getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/**
* Get the diagnostics from config file parsing
*/
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
/**
* Get the syntax diagnostics, for all source files if source file is not supplied
*/
@@ -533,29 +549,29 @@ namespace ts {
/**
* Create the builder to manage semantic diagnostics and cache them
*/
export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, oldProgram?: SemanticDiagnosticsBuilderProgram) {
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, oldProgram));
export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics));
}
/**
* Create the builder that can handle the changes in program and iterate through changed files
* to emit the those files and manage semantic diagnostics cache as well
*/
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram) {
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, oldProgram));
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics));
}
/**
* Creates a builder thats just abstraction over program and can be used with watch
*/
export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram): BuilderProgram;
export function createAbstractBuilder(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram): BuilderProgram;
export function createAbstractBuilder(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | BuilderProgram, oldProgram?: BuilderProgram): BuilderProgram {
const { newProgram: program } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, oldProgram);
export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram;
export function createAbstractBuilder(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram;
export function createAbstractBuilder(newProgramOrRootNames: Program | ReadonlyArray<string>, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram {
const { newProgram: program } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics);
return {
// Only return program, all other methods are not implemented
getProgram: () => program,
@@ -565,6 +581,7 @@ namespace ts {
getSourceFiles: notImplemented,
getOptionsDiagnostics: notImplemented,
getGlobalDiagnostics: notImplemented,
getConfigFileParsingDiagnostics: notImplemented,
getSyntacticDiagnostics: notImplemented,
getSemanticDiagnostics: notImplemented,
emit: notImplemented,
+15 -2
View File
@@ -198,6 +198,7 @@ namespace ts {
export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[] {
const diagnostics = [
...program.getConfigFileParsingDiagnostics(),
...program.getOptionsDiagnostics(cancellationToken),
...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
...program.getGlobalDiagnostics(cancellationToken),
@@ -454,6 +455,12 @@ namespace ts {
}
}
export function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): ReadonlyArray<Diagnostic> {
return configFileParseResult.options.configFile ?
configFileParseResult.options.configFile.parseDiagnostics.concat(configFileParseResult.errors) :
configFileParseResult.errors;
}
/**
* Determined if source file needs to be re-created even if its text hasn't changed
*/
@@ -485,9 +492,10 @@ namespace ts {
* @param options - The compiler options which should be used.
* @param host - The host interacts with the underlying file system.
* @param oldProgram - Reuses an old program structure.
* @param configFileParsingDiagnostics - error during config file parsing
* @returns A 'Program' object.
*/
export function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program {
export function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): Program {
let program: Program;
let files: SourceFile[] = [];
let commonSourceDirectory: string;
@@ -665,7 +673,8 @@ namespace ts {
getSourceFileFromReference,
sourceFileToPackageName,
redirectTargetsSet,
isEmittedFile
isEmittedFile,
getConfigFileParsingDiagnostics
};
verifyCompilerOptions();
@@ -1568,6 +1577,10 @@ namespace ts {
return sortAndDeduplicateDiagnostics(getDiagnosticsProducingTypeChecker().getGlobalDiagnostics().slice());
}
function getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic> {
return configFileParsingDiagnostics || emptyArray;
}
function processRootFile(fileName: string, isDefaultLib: boolean) {
processSourceFile(normalizePath(fileName), isDefaultLib, /*packageId*/ undefined);
}
+4 -7
View File
@@ -117,7 +117,7 @@ namespace ts {
createWatchOfConfigFile(configParseResult, commandLineOptions);
}
else {
performCompilation(configParseResult.fileNames, configParseResult.options);
performCompilation(configParseResult.fileNames, configParseResult.options, getConfigFileParsingDiagnostics(configParseResult));
}
}
else {
@@ -139,11 +139,11 @@ namespace ts {
}
}
function performCompilation(rootFileNames: string[], compilerOptions: CompilerOptions) {
function performCompilation(rootFileNames: string[], compilerOptions: CompilerOptions, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
const compilerHost = createCompilerHost(compilerOptions);
enableStatistics(compilerOptions);
const program = createProgram(rootFileNames, compilerOptions, compilerHost);
const program = createProgram(rootFileNames, compilerOptions, compilerHost, /*oldProgram*/ undefined, configFileParsingDiagnostics);
const exitStatus = emitFilesAndReportErrors(program, reportDiagnostic, s => sys.write(s + sys.newLine));
reportStatistics(program);
return sys.exit(exitStatus);
@@ -169,10 +169,7 @@ namespace ts {
function createWatchOfConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions) {
const watchCompilerHost = createWatchCompilerHostOfConfigFile(configParseResult.options.configFilePath, optionsToExtend, sys, /*createProgram*/ undefined, reportDiagnostic, createWatchStatusReporter(configParseResult.options));
updateWatchCompilationHost(watchCompilerHost);
watchCompilerHost.rootFiles = configParseResult.fileNames;
watchCompilerHost.options = configParseResult.options;
watchCompilerHost.configFileSpecs = configParseResult.configFileSpecs;
watchCompilerHost.configFileWildCardDirectories = configParseResult.wildcardDirectories;
watchCompilerHost.configFileParsingResult = configParseResult;
createWatchProgram(watchCompilerHost);
}
+1
View File
@@ -2671,6 +2671,7 @@ namespace ts {
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
/**
* Gets a type checker that can be used to semantically analyze source files in the program.
+52 -40
View File
@@ -70,10 +70,8 @@ namespace ts {
/** Parses config file using System interface */
export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) {
const host: ParseConfigFileHost = <any>system;
host.onConfigFileDiagnostic = reportDiagnostic;
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(sys, reportDiagnostic, diagnostic);
const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host);
host.onConfigFileDiagnostic = undefined;
host.onUnRecoverableConfigFileDiagnostic = undefined;
return result;
}
@@ -98,13 +96,8 @@ namespace ts {
}
const result = parseJsonText(configFileName, configFileText);
result.parseDiagnostics.forEach(diagnostic => host.onConfigFileDiagnostic(diagnostic));
const cwd = host.getCurrentDirectory();
const configParseResult = parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
configParseResult.errors.forEach(diagnostic => host.onConfigFileDiagnostic(diagnostic));
return configParseResult;
return parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
}
/**
@@ -118,6 +111,7 @@ namespace ts {
getOptionsDiagnostics(): ReadonlyArray<Diagnostic>;
getGlobalDiagnostics(): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(): ReadonlyArray<Diagnostic>;
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
emit(): EmitResult;
}
@@ -126,16 +120,18 @@ namespace ts {
*/
export function emitFilesAndReportErrors(program: ProgramToEmitFilesAndReportErrors, reportDiagnostic: DiagnosticReporter, writeFileName?: (s: string) => void) {
// First get and report any syntactic errors.
const diagnostics = program.getSyntacticDiagnostics().slice();
const diagnostics = program.getConfigFileParsingDiagnostics().slice();
const configFileParsingDiagnosticsLength = diagnostics.length;
addRange(diagnostics, program.getSyntacticDiagnostics());
let reportSemanticDiagnostics = false;
// If we didn't have any syntactic errors, then also try getting the global and
// semantic errors.
if (diagnostics.length === 0) {
if (diagnostics.length === configFileParsingDiagnosticsLength) {
addRange(diagnostics, program.getOptionsDiagnostics());
addRange(diagnostics, program.getGlobalDiagnostics());
if (diagnostics.length === 0) {
if (diagnostics.length === configFileParsingDiagnosticsLength) {
reportSemanticDiagnostics = true;
}
}
@@ -238,7 +234,6 @@ namespace ts {
export function createWatchCompilerHostOfConfigFile<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram<T>, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile<T> {
reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system);
const host = createWatchCompilerHost(system, createProgram, reportDiagnostic, reportWatchStatus) as WatchCompilerHostOfConfigFile<T>;
host.onConfigFileDiagnostic = reportDiagnostic;
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic);
host.configFileName = configFileName;
host.optionsToExtend = optionsToExtend;
@@ -259,7 +254,8 @@ namespace ts {
namespace ts {
export type DiagnosticReporter = (diagnostic: Diagnostic) => void;
export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: T) => T;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
export interface WatchCompilerHost<T extends BuilderProgram> {
/**
* Used to create the program when need for program creation or recreation detected
@@ -345,11 +341,6 @@ namespace ts {
* Reports config file diagnostics
*/
export interface ConfigFileDiagnosticsReporter {
/**
* Reports the diagnostics in reading/writing or parsing of the config file
*/
onConfigFileDiagnostic: DiagnosticReporter;
/**
* Reports unrecoverable error when parsing config file
*/
@@ -378,11 +369,8 @@ namespace ts {
*/
/*@internal*/
export interface WatchCompilerHostOfConfigFile<T extends BuilderProgram> extends WatchCompilerHost<T> {
rootFiles?: string[];
options?: CompilerOptions;
optionsToExtend?: CompilerOptions;
configFileSpecs?: ConfigFileSpecs;
configFileWildCardDirectories?: MapLike<WatchDirectoryFlags>;
configFileParsingResult?: ParsedCommandLine;
}
export interface Watch<T> {
@@ -460,7 +448,10 @@ namespace ts {
const getCurrentDirectory = () => currentDirectory;
const readFile: (path: string, encoding?: string) => string | undefined = (path, encoding) => host.readFile(path, encoding);
const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {}, createProgram } = host;
let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host;
let { rootFiles: rootFileNames, options: compilerOptions } = host;
let configFileSpecs: ConfigFileSpecs;
let configFileParsingDiagnostics: ReadonlyArray<Diagnostic> | undefined;
let hasChangedConfigFileParsingErrors = false;
const cachedDirectoryStructureHost = configFileName && createCachedDirectoryStructureHost(host, currentDirectory, useCaseSensitiveFileNames);
if (cachedDirectoryStructureHost && host.onCachedDirectoryStructureHostCreate) {
@@ -473,13 +464,22 @@ namespace ts {
fileExists: path => host.fileExists(path),
readFile,
getCurrentDirectory,
onConfigFileDiagnostic: host.onConfigFileDiagnostic,
onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic
};
// From tsc we want to get already parsed result and hence check for rootFileNames
if (configFileName && !rootFileNames) {
parseConfigFile();
let newLine = updateNewLine();
reportWatchDiagnostic(Diagnostics.Starting_compilation_in_watch_mode);
if (configFileName) {
newLine = getNewLineCharacter(optionsToExtendForConfigFile, () => host.getNewLine());
if (host.configFileParsingResult) {
setConfigFileParsingResult(host.configFileParsingResult);
}
else {
Debug.assert(!rootFileNames);
parseConfigFile();
}
newLine = updateNewLine();
}
const trace = host.trace && ((s: string) => { host.trace(s + newLine); });
@@ -489,7 +489,6 @@ namespace ts {
const { watchFile, watchFilePath, watchDirectory: watchDirectoryWorker } = getWatchFactory(watchLogLevel, writeLog);
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
let newLine = updateNewLine();
writeLog(`Current directory: ${currentDirectory} CaseSensitiveFileNames: ${useCaseSensitiveFileNames}`);
if (configFileName) {
@@ -546,7 +545,6 @@ namespace ts {
((typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile));
const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives;
reportWatchDiagnostic(Diagnostics.Starting_compilation_in_watch_mode);
synchronizeProgram();
// Update the wild card directory watch
@@ -578,6 +576,10 @@ namespace ts {
// All resolutions are invalid if user provided resolutions
const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(userProvidedResolution);
if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames)) {
if (hasChangedConfigFileParsingErrors) {
builderProgram = createProgram(/*rootNames*/ undefined, /*options*/ undefined, compilerHost, builderProgram, configFileParsingDiagnostics);
hasChangedConfigFileParsingErrors = false;
}
return builderProgram;
}
@@ -590,10 +592,11 @@ namespace ts {
const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !program;
hasChangedCompilerOptions = false;
hasChangedConfigFileParsingErrors = false;
resolutionCache.startCachingPerDirectoryResolution();
compilerHost.hasInvalidatedResolution = hasInvalidatedResolution;
compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames;
builderProgram = createProgram(rootFileNames, compilerOptions, compilerHost, builderProgram);
builderProgram = createProgram(rootFileNames, compilerOptions, compilerHost, builderProgram, configFileParsingDiagnostics);
resolutionCache.finishCachingPerDirectoryResolution();
// Update watches
@@ -630,7 +633,7 @@ namespace ts {
}
function updateNewLine() {
return getNewLineCharacter(compilerOptions, () => host.getNewLine());
return getNewLineCharacter(compilerOptions || optionsToExtendForConfigFile, () => host.getNewLine());
}
function toPath(fileName: string) {
@@ -760,7 +763,7 @@ namespace ts {
function reportWatchDiagnostic(message: DiagnosticMessage) {
if (host.onWatchStatusChange) {
host.onWatchStatusChange(createCompilerDiagnostic(message), newLine, compilerOptions);
host.onWatchStatusChange(createCompilerDiagnostic(message), newLine, compilerOptions || optionsToExtendForConfigFile);
}
}
@@ -801,8 +804,13 @@ namespace ts {
function reloadFileNamesFromConfigFile() {
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, parseConfigFileHost);
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
host.onConfigFileDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
if (result.fileNames.length) {
configFileParsingDiagnostics = filter(configFileParsingDiagnostics, error => !isErrorNoInputFiles(error));
hasChangedConfigFileParsingErrors = true;
}
else if (!configFileSpecs.filesSpecs && !some(configFileParsingDiagnostics, isErrorNoInputFiles)) {
configFileParsingDiagnostics = configFileParsingDiagnostics!.concat(getErrorForNoInputFiles(configFileSpecs, configFileName));
hasChangedConfigFileParsingErrors = true;
}
rootFileNames = result.fileNames;
@@ -826,11 +834,15 @@ namespace ts {
}
function parseConfigFile() {
const configParseResult = getParsedCommandLineOfConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost);
rootFileNames = configParseResult.fileNames;
compilerOptions = configParseResult.options;
configFileSpecs = configParseResult.configFileSpecs;
configFileWildCardDirectories = configParseResult.wildcardDirectories;
setConfigFileParsingResult(getParsedCommandLineOfConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost));
}
function setConfigFileParsingResult(configFileParseResult: ParsedCommandLine) {
rootFileNames = configFileParseResult.fileNames;
compilerOptions = configFileParseResult.options;
configFileSpecs = configFileParseResult.configFileSpecs;
configFileParsingDiagnostics = getConfigFileParsingDiagnostics(configFileParseResult);
hasChangedConfigFileParsingErrors = true;
}
function onSourceFileChange(fileName: string, eventKind: FileWatcherEventKind, path: Path) {
@@ -876,10 +888,10 @@ namespace ts {
}
function watchConfigFileWildCardDirectories() {
if (configFileWildCardDirectories) {
if (configFileSpecs) {
updateWatchingWildcardDirectories(
watchedWildcardDirectories || (watchedWildcardDirectories = createMap()),
createMapFromTemplate(configFileWildCardDirectories),
createMapFromTemplate(configFileSpecs.wildcardDirectories),
watchWildcardDirectory
);
}
+50 -35
View File
@@ -72,22 +72,29 @@ namespace ts.tscWatch {
checkOutputDoesNotContain(host, expectedNonAffectedFiles);
}
const elapsedRegex = /^Elapsed:: [0-9]+ms/;
function checkOutputErrors(
host: WatchedSystem,
preErrorsWatchDiagnostic: DiagnosticMessage | undefined,
logsBeforeWatchDiagnostic: string[] | undefined,
preErrorsWatchDiagnostic: DiagnosticMessage,
logsBeforeErrors: string[] | undefined,
errors: ReadonlyArray<Diagnostic>,
disableConsoleClears?: boolean | undefined,
...postErrorsWatchDiagnostics: DiagnosticMessage[]
) {
let screenClears = 0;
const outputs = host.getOutput();
const expectedOutputCount = (preErrorsWatchDiagnostic ? 1 : 0) + errors.length + postErrorsWatchDiagnostics.length;
assert.equal(outputs.length, expectedOutputCount);
const expectedOutputCount = 1 + errors.length + postErrorsWatchDiagnostics.length +
(logsBeforeWatchDiagnostic ? logsBeforeWatchDiagnostic.length : 0) + (logsBeforeErrors ? logsBeforeErrors.length : 0);
assert.equal(outputs.length, expectedOutputCount, JSON.stringify(outputs));
let index = 0;
if (preErrorsWatchDiagnostic) {
assertWatchDiagnostic(preErrorsWatchDiagnostic);
}
forEach(logsBeforeWatchDiagnostic, log => assertLog("logsBeforeWatchDiagnostic", log));
assertWatchDiagnostic(preErrorsWatchDiagnostic);
forEach(logsBeforeErrors, log => assertLog("logBeforeError", log));
// Verify errors
forEach(errors, assertDiagnostic);
forEach(postErrorsWatchDiagnostics, assertWatchDiagnostic);
assert.equal(host.screenClears.length, screenClears, "Expected number of screen clears");
host.clearOutput();
function assertDiagnostic(diagnostic: Diagnostic) {
@@ -96,8 +103,18 @@ namespace ts.tscWatch {
index++;
}
function assertLog(caption: string, expected: string) {
const actual = outputs[index];
assert.equal(actual.replace(elapsedRegex, ""), expected.replace(elapsedRegex, ""), getOutputAtFailedMessage(caption, expected));
index++;
}
function assertWatchDiagnostic(diagnosticMessage: DiagnosticMessage) {
const expected = getWatchDiagnosticWithoutDate(diagnosticMessage);
if (!disableConsoleClears && diagnosticMessage.code !== Diagnostics.Compilation_complete_Watching_for_file_changes.code) {
assert.equal(host.screenClears[screenClears], index, `Expected screen clear at this diagnostic: ${expected}`);
screenClears++;
}
assert.isTrue(endsWith(outputs[index], expected), getOutputAtFailedMessage("Watch diagnostic", expected));
index++;
}
@@ -111,20 +128,16 @@ namespace ts.tscWatch {
}
}
function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>) {
checkOutputErrors(host, Diagnostics.Starting_compilation_in_watch_mode, errors, Diagnostics.Compilation_complete_Watching_for_file_changes);
function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) {
checkOutputErrors(host, /*logsBeforeWatchDiagnostic*/ undefined, Diagnostics.Starting_compilation_in_watch_mode, logsBeforeErrors, errors, disableConsoleClears, Diagnostics.Compilation_complete_Watching_for_file_changes);
}
function checkOutputErrorsInitialWithConfigErrors(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>) {
checkOutputErrors(host, /*preErrorsWatchDiagnostic*/ undefined, errors, Diagnostics.Starting_compilation_in_watch_mode, Diagnostics.Compilation_complete_Watching_for_file_changes);
function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
checkOutputErrors(host, logsBeforeWatchDiagnostic, Diagnostics.File_change_detected_Starting_incremental_compilation, logsBeforeErrors, errors, disableConsoleClears, Diagnostics.Compilation_complete_Watching_for_file_changes);
}
function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>) {
checkOutputErrors(host, Diagnostics.File_change_detected_Starting_incremental_compilation, errors, Diagnostics.Compilation_complete_Watching_for_file_changes);
}
function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, expectedExitCode: ExitStatus) {
checkOutputErrors(host, Diagnostics.File_change_detected_Starting_incremental_compilation, errors);
function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, expectedExitCode: ExitStatus, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
checkOutputErrors(host, logsBeforeWatchDiagnostic, Diagnostics.File_change_detected_Starting_incremental_compilation, logsBeforeErrors, errors, disableConsoleClears);
assert.equal(host.exitCode, expectedExitCode);
}
@@ -897,7 +910,7 @@ namespace ts.tscWatch {
const host = createWatchedSystem([file, configFile, libFile]);
const watch = createWatchOfConfigFile(configFile.path, host);
checkOutputErrorsInitialWithConfigErrors(host, [
checkOutputErrorsInitial(host, [
getUnknownCompilerOption(watch(), configFile, "foo"),
getUnknownCompilerOption(watch(), configFile, "allowJS")
]);
@@ -2183,30 +2196,32 @@ declare module "fs" {
path: "f.ts",
content: ""
};
const files = [file];
const files = [file, libFile];
const disableConsoleClear = options.diagnostics || options.extendedDiagnostics || options.preserveWatchOutput;
const host = createWatchedSystem(files);
let clearCount: number | undefined;
checkConsoleClears();
createWatchOfFilesAndCompilerOptions([file.path], host, options);
checkConsoleClears();
checkOutputErrorsInitial(host, emptyArray, disableConsoleClear, options.extendedDiagnostics && [
"Current directory: / CaseSensitiveFileNames: false\n",
"Synchronizing program\n",
"CreatingProgramWith::\n",
" roots: [\"f.ts\"]\n",
" options: {\"extendedDiagnostics\":true}\n",
"FileWatcher:: Added:: WatchInfo: f.ts 250 \n",
"FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 \n"
]);
file.content = "//";
host.reloadFS(files);
host.runQueuedTimeoutCallbacks();
checkConsoleClears();
function checkConsoleClears() {
if (clearCount === undefined || options.preserveWatchOutput) {
clearCount = 0;
}
else if (!options.diagnostics && !options.extendedDiagnostics) {
clearCount++;
}
host.checkScreenClears(clearCount);
return clearCount;
}
checkOutputErrorsIncremental(host, emptyArray, disableConsoleClear, options.extendedDiagnostics && [
"FileWatcher:: Triggered with /f.ts1:: WatchInfo: f.ts 250 \n",
"Elapsed:: 0ms FileWatcher:: Triggered with /f.ts1:: WatchInfo: f.ts 250 \n"
], options.extendedDiagnostics && [
"Synchronizing program\n",
"CreatingProgramWith::\n",
" roots: [\"f.ts\"]\n",
" options: {\"extendedDiagnostics\":true}\n"
]);
}
it("without --diagnostics or --extendedDiagnostics", () => {
+3 -6
View File
@@ -288,7 +288,7 @@ interface Array<T> {}`
private toPath: (f: string) => Path;
private timeoutCallbacks = new Callbacks();
private immediateCallbacks = new Callbacks();
private screenClears = 0;
readonly screenClears: number[] = [];
readonly watchedDirectories = createMultiMap<TestDirectoryWatcher>();
readonly watchedDirectoriesRecursive = createMultiMap<TestDirectoryWatcher>();
@@ -781,7 +781,7 @@ interface Array<T> {}`
}
clearScreen(): void {
this.screenClears += 1;
this.screenClears.push(this.output.length);
}
checkTimeoutQueueLengthAndRun(expected: number) {
@@ -821,10 +821,6 @@ interface Array<T> {}`
this.immediateCallbacks.unregister(timeoutId);
}
checkScreenClears(expected: number): void {
assert.equal(this.screenClears, expected);
}
createDirectory(directoryName: string): void {
const folder = this.toFolder(directoryName);
@@ -858,6 +854,7 @@ interface Array<T> {}`
clearOutput() {
clear(this.output);
this.screenClears.length = 0;
}
realpath(s: string): string {
+4 -1
View File
@@ -1689,6 +1689,7 @@ declare namespace ts {
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
/**
* Gets a type checker that can be used to semantically analyze source files in the program.
*/
@@ -3930,6 +3931,7 @@ declare namespace ts {
function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string;
function formatDiagnosticsWithColorAndContext(diagnostics: ReadonlyArray<Diagnostic>, host: FormatDiagnosticsHost): string;
function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string;
function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): ReadonlyArray<Diagnostic>;
/**
* Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions'
* that represent a compilation unit.
@@ -3941,9 +3943,10 @@ declare namespace ts {
* @param options - The compiler options which should be used.
* @param host - The host interacts with the underlying file system.
* @param oldProgram - Reuses an old program structure.
* @param configFileParsingDiagnostics - error during config file parsing
* @returns A 'Program' object.
*/
function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program;
function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): Program;
}
declare namespace ts {
interface Node {
+16 -12
View File
@@ -1689,6 +1689,7 @@ declare namespace ts {
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
/**
* Gets a type checker that can be used to semantically analyze source files in the program.
*/
@@ -3877,6 +3878,7 @@ declare namespace ts {
function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string;
function formatDiagnosticsWithColorAndContext(diagnostics: ReadonlyArray<Diagnostic>, host: FormatDiagnosticsHost): string;
function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string;
function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): ReadonlyArray<Diagnostic>;
/**
* Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions'
* that represent a compilation unit.
@@ -3888,9 +3890,10 @@ declare namespace ts {
* @param options - The compiler options which should be used.
* @param host - The host interacts with the underlying file system.
* @param oldProgram - Reuses an old program structure.
* @param configFileParsingDiagnostics - error during config file parsing
* @returns A 'Program' object.
*/
function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program;
function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): Program;
}
declare namespace ts {
interface EmitOutput {
@@ -3951,6 +3954,10 @@ declare namespace ts {
* Get the diagnostics that dont belong to any file
*/
getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
/**
* Get the diagnostics from config file parsing
*/
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;
/**
* Get the syntax diagnostics, for all source files if source file is not supplied
*/
@@ -4010,24 +4017,25 @@ declare namespace ts {
/**
* Create the builder to manage semantic diagnostics and cache them
*/
function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram;
function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram;
function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): SemanticDiagnosticsBuilderProgram;
function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): SemanticDiagnosticsBuilderProgram;
/**
* Create the builder that can handle the changes in program and iterate through changed files
* to emit the those files and manage semantic diagnostics cache as well
*/
function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram;
function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram;
function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): EmitAndSemanticDiagnosticsBuilderProgram;
function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): EmitAndSemanticDiagnosticsBuilderProgram;
/**
* Creates a builder thats just abstraction over program and can be used with watch
*/
function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram): BuilderProgram;
function createAbstractBuilder(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram): BuilderProgram;
function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram;
function createAbstractBuilder(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram;
}
declare namespace ts {
type DiagnosticReporter = (diagnostic: Diagnostic) => void;
type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: T) => T;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
interface WatchCompilerHost<T extends BuilderProgram> {
/**
* Used to create the program when need for program creation or recreation detected
@@ -4091,10 +4099,6 @@ declare namespace ts {
* Reports config file diagnostics
*/
interface ConfigFileDiagnosticsReporter {
/**
* Reports the diagnostics in reading/writing or parsing of the config file
*/
onConfigFileDiagnostic: DiagnosticReporter;
/**
* Reports unrecoverable error when parsing config file
*/