Keep track of why files are in the program (#40011)

* --explainFiles currently hardcoded

* Move configFileSpecs to configFile so it can be used in program later

* Explain root file inclusion reason and explain include files in the log

* Baseline explainFiles

* Fix incorrectly reporting of file list two times in --b mode

* Fix unnecessary new lines in output represented incorretly in the baseline

* More tests

* More cleaning up

* Keep listing files in same order as list files, just add explaination

* Fix double listing of file names when the program has errors

* Make diagnostic chains for file include reason

* Add explaination for the file include to diagnostics for program

* Harness ls incorrectly adding tsconfig as the root file

* Fix incorrect use of path for calculating absolute path

* Fix the root file in fourslash

* Test project service options merge

* Add config file name to matched by include explaination

* Add test for when the file changes and program is reused completely but related file information is reattached to correct location

* Handle file preprocessing diagnostics updates when program is reused and related information location changes

* Moved types to types.ts

* Refactoring and cleanup

* More cleanup

* More refatoring

* Handle synthetic imports

* Baselines after merge
This commit is contained in:
Sheetal Nandi
2020-12-08 16:10:05 -08:00
committed by GitHub
parent 49d7de17d6
commit 2eca17d7c1
84 changed files with 7013 additions and 723 deletions
+109 -127
View File
@@ -196,6 +196,11 @@ namespace ts {
description: Diagnostics.Print_names_of_files_part_of_the_compilation
},
{
name: "explainFiles",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Print_names_of_files_and_the_reason_they_are_part_of_the_compilation
}, {
name: "listEmittedFiles",
type: "boolean",
category: Diagnostics.Advanced_Options,
@@ -268,6 +273,31 @@ namespace ts {
},
];
/* @internal */
export const targetOptionDeclaration: CommandLineOptionOfCustomType = {
name: "target",
shortName: "t",
type: new Map(getEntries({
es3: ScriptTarget.ES3,
es5: ScriptTarget.ES5,
es6: ScriptTarget.ES2015,
es2015: ScriptTarget.ES2015,
es2016: ScriptTarget.ES2016,
es2017: ScriptTarget.ES2017,
es2018: ScriptTarget.ES2018,
es2019: ScriptTarget.ES2019,
es2020: ScriptTarget.ES2020,
esnext: ScriptTarget.ESNext,
})),
affectsSourceFile: true,
affectsModuleResolution: true,
affectsEmit: true,
paramType: Diagnostics.VERSION,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_ES2015_ES2016_ES2017_ES2018_ES2019_ES2020_or_ESNEXT,
};
/* @internal */
export const optionDeclarations: CommandLineOption[] = [
// CommandLine only options
@@ -330,29 +360,7 @@ namespace ts {
},
// Basic
{
name: "target",
shortName: "t",
type: new Map(getEntries({
es3: ScriptTarget.ES3,
es5: ScriptTarget.ES5,
es6: ScriptTarget.ES2015,
es2015: ScriptTarget.ES2015,
es2016: ScriptTarget.ES2016,
es2017: ScriptTarget.ES2017,
es2018: ScriptTarget.ES2018,
es2019: ScriptTarget.ES2019,
es2020: ScriptTarget.ES2020,
esnext: ScriptTarget.ESNext,
})),
affectsSourceFile: true,
affectsModuleResolution: true,
affectsEmit: true,
paramType: Diagnostics.VERSION,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_ES2015_ES2016_ES2017_ES2018_ES2019_ES2020_or_ESNEXT,
},
targetOptionDeclaration,
{
name: "module",
shortName: "m",
@@ -2030,10 +2038,10 @@ namespace ts {
const files = map(
filter(
configParseResult.fileNames,
(!configParseResult.configFileSpecs || !configParseResult.configFileSpecs.validatedIncludeSpecs) ? _ => true : matchesSpecs(
!configParseResult.options.configFile?.configFileSpecs?.validatedIncludeSpecs ? returnTrue : matchesSpecs(
configFileName,
configParseResult.configFileSpecs.validatedIncludeSpecs,
configParseResult.configFileSpecs.validatedExcludeSpecs,
configParseResult.options.configFile.configFileSpecs.validatedIncludeSpecs,
configParseResult.options.configFile.configFileSpecs.validatedExcludeSpecs,
host,
)
),
@@ -2058,9 +2066,9 @@ namespace ts {
watchOptions: watchOptionMap && optionMapToObject(watchOptionMap),
references: map(configParseResult.projectReferences, r => ({ ...r, path: r.originalPath ? r.originalPath : "", originalPath: undefined })),
files: length(files) ? files : undefined,
...(configParseResult.configFileSpecs ? {
include: filterSameAsDefaultInclude(configParseResult.configFileSpecs.validatedIncludeSpecs),
exclude: configParseResult.configFileSpecs.validatedExcludeSpecs
...(configParseResult.options.configFile?.configFileSpecs ? {
include: filterSameAsDefaultInclude(configParseResult.options.configFile.configFileSpecs.validatedIncludeSpecs),
exclude: configParseResult.options.configFile.configFileSpecs.validatedExcludeSpecs
} : {}),
compileOnSave: !!configParseResult.compileOnSave ? true : undefined
};
@@ -2081,7 +2089,7 @@ namespace ts {
}
function matchesSpecs(path: string, includeSpecs: readonly string[] | undefined, excludeSpecs: readonly string[] | undefined, host: ConvertToTSConfigHost): (path: string) => boolean {
if (!includeSpecs) return _ => true;
if (!includeSpecs) return returnTrue;
const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, host.useCaseSensitiveFileNames, host.getCurrentDirectory());
const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, host.useCaseSensitiveFileNames);
const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, host.useCaseSensitiveFileNames);
@@ -2094,7 +2102,7 @@ namespace ts {
if (excludeRe) {
return path => excludeRe.test(path);
}
return _ => true;
return returnTrue;
}
function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): ESMap<string, string | number> | undefined {
@@ -2387,40 +2395,29 @@ namespace ts {
parsedConfig.watchOptions || existingWatchOptions;
options.configFilePath = configFileName && normalizeSlashes(configFileName);
const configFileSpecs = getConfigFileSpecs();
if (sourceFile) sourceFile.configFileSpecs = configFileSpecs;
setConfigFileInOptions(options, sourceFile);
let projectReferences: ProjectReference[] | undefined;
const { fileNames, wildcardDirectories, spec } = getFileNames();
const basePathForFileNames = normalizePath(configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath);
return {
options,
watchOptions,
fileNames,
projectReferences,
fileNames: getFileNames(basePathForFileNames),
projectReferences: getProjectReferences(basePathForFileNames),
typeAcquisition: parsedConfig.typeAcquisition || getDefaultTypeAcquisition(),
raw,
errors,
wildcardDirectories,
// Wildcard directories (provided as part of a wildcard path) are stored in a
// file map that marks whether it was a regular wildcard match (with a `*` or `?` token),
// or a recursive directory. This information is used by filesystem watchers to monitor for
// new entries in these paths.
wildcardDirectories: getWildcardDirectories(configFileSpecs, basePathForFileNames, host.useCaseSensitiveFileNames),
compileOnSave: !!raw.compileOnSave,
configFileSpecs: spec
};
function getFileNames(): ExpandResult {
function getConfigFileSpecs(): ConfigFileSpecs {
const referencesOfRaw = getPropFromRaw<ProjectReference>("references", element => typeof element === "object", "object");
if (isArray(referencesOfRaw)) {
for (const ref of referencesOfRaw) {
if (typeof ref.path !== "string") {
createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "reference.path", "string");
}
else {
(projectReferences || (projectReferences = [])).push({
path: getNormalizedAbsolutePath(ref.path, basePath),
originalPath: ref.path,
prepend: ref.prepend,
circular: ref.circular
});
}
}
}
const filesSpecs = toPropValue(getSpecsFromRaw("files"));
if (filesSpecs) {
const hasZeroOrNoReferences = referencesOfRaw === "no-prop" || isArray(referencesOfRaw) && referencesOfRaw.length === 0;
@@ -2457,13 +2454,57 @@ namespace ts {
if (filesSpecs === undefined && includeSpecs === undefined) {
includeSpecs = ["**/*"];
}
let validatedIncludeSpecs: readonly string[] | undefined, validatedExcludeSpecs: readonly string[] | undefined;
const result = matchFileNames(filesSpecs, includeSpecs, excludeSpecs, configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath, options, host, errors, extraFileExtensions, sourceFile);
if (shouldReportNoInputFiles(result, canJsonReportNoInputFiles(raw), resolutionStack)) {
errors.push(getErrorForNoInputFiles(result.spec, configFileName));
// The exclude spec list is converted into a regular expression, which allows us to quickly
// test whether a file or directory should be excluded before recursively traversing the
// file system.
if (includeSpecs) {
validatedIncludeSpecs = validateSpecs(includeSpecs, errors, /*disallowTrailingRecursion*/ true, sourceFile, "include");
}
return result;
if (excludeSpecs) {
validatedExcludeSpecs = validateSpecs(excludeSpecs, errors, /*disallowTrailingRecursion*/ false, sourceFile, "exclude");
}
return {
filesSpecs,
includeSpecs,
excludeSpecs,
validatedFilesSpec: filter(filesSpecs, isString),
validatedIncludeSpecs,
validatedExcludeSpecs,
};
}
function getFileNames(basePath: string): string[] {
const fileNames = getFileNamesFromConfigSpecs(configFileSpecs, basePath, options, host, extraFileExtensions);
if (shouldReportNoInputFiles(fileNames, canJsonReportNoInputFiles(raw), resolutionStack)) {
errors.push(getErrorForNoInputFiles(configFileSpecs, configFileName));
}
return fileNames;
}
function getProjectReferences(basePath: string): readonly ProjectReference[] | undefined {
let projectReferences: ProjectReference[] | undefined;
const referencesOfRaw = getPropFromRaw<ProjectReference>("references", element => typeof element === "object", "object");
if (isArray(referencesOfRaw)) {
for (const ref of referencesOfRaw) {
if (typeof ref.path !== "string") {
createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "reference.path", "string");
}
else {
(projectReferences || (projectReferences = [])).push({
path: getNormalizedAbsolutePath(ref.path, basePath),
originalPath: ref.path,
prepend: ref.prepend,
circular: ref.circular
});
}
}
}
return projectReferences;
}
type PropOfRaw<T> = readonly T[] | "not-array" | "no-prop";
@@ -2511,8 +2552,8 @@ namespace ts {
JSON.stringify(excludeSpecs || []));
}
function shouldReportNoInputFiles(result: ExpandResult, canJsonReportNoInutFiles: boolean, resolutionStack?: Path[]) {
return result.fileNames.length === 0 && canJsonReportNoInutFiles && (!resolutionStack || resolutionStack.length === 0);
function shouldReportNoInputFiles(fileNames: string[], canJsonReportNoInutFiles: boolean, resolutionStack?: Path[]) {
return fileNames.length === 0 && canJsonReportNoInutFiles && (!resolutionStack || resolutionStack.length === 0);
}
/*@internal*/
@@ -2521,9 +2562,9 @@ namespace ts {
}
/*@internal*/
export function updateErrorForNoInputFiles(result: ExpandResult, configFileName: string, configFileSpecs: ConfigFileSpecs, configParseDiagnostics: Diagnostic[], canJsonReportNoInutFiles: boolean) {
export function updateErrorForNoInputFiles(fileNames: string[], configFileName: string, configFileSpecs: ConfigFileSpecs, configParseDiagnostics: Diagnostic[], canJsonReportNoInutFiles: boolean) {
const existingErrors = configParseDiagnostics.length;
if (shouldReportNoInputFiles(result, canJsonReportNoInutFiles)) {
if (shouldReportNoInputFiles(fileNames, canJsonReportNoInutFiles)) {
configParseDiagnostics.push(getErrorForNoInputFiles(configFileSpecs, configFileName));
}
else {
@@ -3011,65 +3052,10 @@ namespace ts {
*/
const wildcardDirectoryPattern = /^[^*?]*(?=\/[^/]*[*?])/;
/**
* Expands an array of file specifications.
*
* @param filesSpecs The literal file names to include.
* @param includeSpecs The wildcard file specifications to include.
* @param excludeSpecs The wildcard file specifications to exclude.
* @param basePath The base path for any relative file specifications.
* @param options Compiler options.
* @param host The host used to resolve files and directories.
* @param errors An array for diagnostic reporting.
*/
function matchFileNames(
filesSpecs: readonly string[] | undefined,
includeSpecs: readonly string[] | undefined,
excludeSpecs: readonly string[] | undefined,
basePath: string,
options: CompilerOptions,
host: ParseConfigHost,
errors: Push<Diagnostic>,
extraFileExtensions: readonly FileExtensionInfo[],
jsonSourceFile: TsConfigSourceFile | undefined
): ExpandResult {
basePath = normalizePath(basePath);
let validatedIncludeSpecs: readonly string[] | undefined, validatedExcludeSpecs: readonly string[] | undefined;
// The exclude spec list is converted into a regular expression, which allows us to quickly
// test whether a file or directory should be excluded before recursively traversing the
// file system.
if (includeSpecs) {
validatedIncludeSpecs = validateSpecs(includeSpecs, errors, /*disallowTrailingRecursion*/ true, jsonSourceFile, "include");
}
if (excludeSpecs) {
validatedExcludeSpecs = validateSpecs(excludeSpecs, errors, /*disallowTrailingRecursion*/ false, jsonSourceFile, "exclude");
}
// Wildcard directories (provided as part of a wildcard path) are stored in a
// file map that marks whether it was a regular wildcard match (with a `*` or `?` token),
// or a recursive directory. This information is used by filesystem watchers to monitor for
// new entries in these paths.
const wildcardDirectories = getWildcardDirectories(validatedIncludeSpecs, validatedExcludeSpecs, basePath, host.useCaseSensitiveFileNames);
const spec: ConfigFileSpecs = {
filesSpecs,
includeSpecs,
excludeSpecs,
validatedFilesSpec: filter(filesSpecs, isString),
validatedIncludeSpecs,
validatedExcludeSpecs,
wildcardDirectories
};
return getFileNamesFromConfigSpecs(spec, basePath, options, host, extraFileExtensions);
}
/**
* Gets the file names from the provided config file specs that contain, files, include, exclude and
* other properties needed to resolve the file names
* @param spec The config file specs extracted with file names to include, wildcards to include/exclude and other details
* @param configFileSpecs The config file specs extracted with file names to include, wildcards to include/exclude and other details
* @param basePath The base path for any relative file specifications.
* @param options Compiler options.
* @param host The host used to resolve files and directories.
@@ -3077,12 +3063,12 @@ namespace ts {
*/
/* @internal */
export function getFileNamesFromConfigSpecs(
spec: ConfigFileSpecs,
configFileSpecs: ConfigFileSpecs,
basePath: string,
options: CompilerOptions,
host: ParseConfigHost,
extraFileExtensions: readonly FileExtensionInfo[] = emptyArray
): ExpandResult {
): string[] {
basePath = normalizePath(basePath);
const keyMapper = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
@@ -3101,7 +3087,7 @@ namespace ts {
// file map with a possibly case insensitive key. We use this map to store paths matched
// via wildcard of *.json kind
const wildCardJsonFileMap = new Map<string, string>();
const { validatedFilesSpec, validatedIncludeSpecs, validatedExcludeSpecs, wildcardDirectories } = spec;
const { validatedFilesSpec, validatedIncludeSpecs, validatedExcludeSpecs } = configFileSpecs;
// Rather than requery this for each file and filespec, we query the supported extensions
// once and store it on the expansion context.
@@ -3162,11 +3148,7 @@ namespace ts {
const literalFiles = arrayFrom(literalFileMap.values());
const wildcardFiles = arrayFrom(wildcardFileMap.values());
return {
fileNames: literalFiles.concat(wildcardFiles, arrayFrom(wildCardJsonFileMap.values())),
wildcardDirectories,
spec
};
return literalFiles.concat(wildcardFiles, arrayFrom(wildCardJsonFileMap.values()));
}
/* @internal */
@@ -3251,7 +3233,7 @@ namespace ts {
/**
* Gets directories in a set of include patterns that should be watched for changes.
*/
function getWildcardDirectories(include: readonly string[] | undefined, exclude: readonly string[] | undefined, path: string, useCaseSensitiveFileNames: boolean): MapLike<WatchDirectoryFlags> {
function getWildcardDirectories({ validatedIncludeSpecs: include, validatedExcludeSpecs: exclude }: ConfigFileSpecs, path: string, useCaseSensitiveFileNames: boolean): MapLike<WatchDirectoryFlags> {
// We watch a directory recursively if it contains a wildcard anywhere in a directory segment
// of the pattern:
//
+156
View File
@@ -1196,6 +1196,158 @@
"category": "Error",
"code": 1392
},
"Imported via {0} from file '{1}'": {
"category": "Message",
"code": 1393
},
"Imported via {0} from file '{1}' with packageId '{2}'": {
"category": "Message",
"code": 1394
},
"Imported via {0} from file '{1}' to import 'importHelpers' as specified in compilerOptions": {
"category": "Message",
"code": 1395
},
"Imported via {0} from file '{1}' with packageId '{2}' to import 'importHelpers' as specified in compilerOptions": {
"category": "Message",
"code": 1396
},
"Imported via {0} from file '{1}' to import 'jsx' and 'jsxs' factory functions": {
"category": "Message",
"code": 1397
},
"Imported via {0} from file '{1}' with packageId '{2}' to import 'jsx' and 'jsxs' factory functions": {
"category": "Message",
"code": 1398
},
"File is included via import here.": {
"category": "Message",
"code": 1399
},
"Referenced via '{0}' from file '{1}'": {
"category": "Message",
"code": 1400
},
"File is included via reference here.": {
"category": "Message",
"code": 1401
},
"Type library referenced via '{0}' from file '{1}'": {
"category": "Message",
"code": 1402
},
"Type library referenced via '{0}' from file '{1}' with packageId '{2}'": {
"category": "Message",
"code": 1403
},
"File is included via type library reference here.": {
"category": "Message",
"code": 1404
},
"Library referenced via '{0}' from file '{1}'": {
"category": "Message",
"code": 1405
},
"File is included via library reference here.": {
"category": "Message",
"code": 1406
},
"Matched by include pattern '{0}' in '{1}'": {
"category": "Message",
"code": 1407
},
"File is matched by include pattern specified here.": {
"category": "Message",
"code": 1408
},
"Part of 'files' list in tsconfig.json": {
"category": "Message",
"code": 1409
},
"File is matched by 'files' list specified here.": {
"category": "Message",
"code": 1410
},
"Output from referenced project '{0}' included because '{1}' specified": {
"category": "Message",
"code": 1411
},
"Output from referenced project '{0}' included because '--module' is specified as 'none'": {
"category": "Message",
"code": 1412
},
"File is output from referenced project specified here.": {
"category": "Message",
"code": 1413
},
"Source from referenced project '{0}' included because '{1}' specified": {
"category": "Message",
"code": 1414
},
"Source from referenced project '{0}' included because '--module' is specified as 'none'": {
"category": "Message",
"code": 1415
},
"File is source from referenced project specified here.": {
"category": "Message",
"code": 1416
},
"Entry point of type library '{0}' specified in compilerOptions": {
"category": "Message",
"code": 1417
},
"Entry point of type library '{0}' specified in compilerOptions with packageId '{1}'": {
"category": "Message",
"code": 1418
},
"File is entry point of type library specified here.": {
"category": "Message",
"code": 1419
},
"Entry point for implicit type library '{0}'": {
"category": "Message",
"code": 1420
},
"Entry point for implicit type library '{0}' with packageId '{1}'": {
"category": "Message",
"code": 1421
},
"Library '{0}' specified in compilerOptions": {
"category": "Message",
"code": 1422
},
"File is library specified here.": {
"category": "Message",
"code": 1423
},
"Default library": {
"category": "Message",
"code": 1424
},
"Default library for target '{0}'": {
"category": "Message",
"code": 1425
},
"File is default library for target specified here.": {
"category": "Message",
"code": 1426
},
"Root file specified for compilation": {
"category": "Message",
"code": 1427
},
"File is output of project reference source '{0}'": {
"category": "Message",
"code": 1428
},
"File redirects to file '{0}'": {
"category": "Message",
"code": 1429
},
"The file is in the program because:": {
"category": "Message",
"code": 1430
},
"The types of '{0}' are incompatible between these types.": {
"category": "Error",
"code": 2200
@@ -4748,6 +4900,10 @@
"category": "Error",
"code": 6504
},
"Print names of files and the reason they are part of the compilation.": {
"category": "Message",
"code": 6505
},
"Require undeclared properties from index signatures to use element accesses.": {
"category": "Error",
"code": 6803
+337 -245
View File
@@ -466,20 +466,19 @@ namespace ts {
if (diagnostic.file) {
output += host.getNewLine();
output += formatCodeSpan(diagnostic.file, diagnostic.start!, diagnostic.length!, "", getCategoryFormat(diagnostic.category), host); // TODO: GH#18217
if (diagnostic.relatedInformation) {
output += host.getNewLine();
for (const { file, start, length, messageText } of diagnostic.relatedInformation) {
if (file) {
output += host.getNewLine();
output += halfIndent + formatLocation(file, start!, host); // TODO: GH#18217
output += formatCodeSpan(file, start!, length!, indent, ForegroundColorEscapeSequences.Cyan, host); // TODO: GH#18217
}
}
if (diagnostic.relatedInformation) {
output += host.getNewLine();
for (const { file, start, length, messageText } of diagnostic.relatedInformation) {
if (file) {
output += host.getNewLine();
output += indent + flattenDiagnosticMessageText(messageText, host.getNewLine());
output += halfIndent + formatLocation(file, start!, host); // TODO: GH#18217
output += formatCodeSpan(file, start!, length!, indent, ForegroundColorEscapeSequences.Cyan, host); // TODO: GH#18217
}
output += host.getNewLine();
output += indent + flattenDiagnosticMessageText(messageText, host.getNewLine());
}
}
output += host.getNewLine();
}
return output;
@@ -583,10 +582,66 @@ namespace ts {
allDiagnostics?: readonly T[];
}
interface RefFile extends TextRange {
kind: RefFileKind;
index: number;
/*@internal*/
export function isReferencedFile(reason: FileIncludeReason | undefined): reason is ReferencedFile {
switch (reason?.kind) {
case FileIncludeKind.Import:
case FileIncludeKind.ReferenceFile:
case FileIncludeKind.TypeReferenceDirective:
case FileIncludeKind.LibReferenceDirective:
return true;
default:
return false;
}
}
/*@internal*/
export interface ReferenceFileLocation {
file: SourceFile;
pos: number;
end: number;
packageId: PackageId | undefined;
}
/*@internal*/
export interface SyntheticReferenceFileLocation {
file: SourceFile;
packageId: PackageId | undefined;
text: string;
}
/*@internal*/
export function isReferenceFileLocation(location: ReferenceFileLocation | SyntheticReferenceFileLocation): location is ReferenceFileLocation {
return (location as ReferenceFileLocation).pos !== undefined;
}
/*@internal*/
export function getReferencedFileLocation(getSourceFileByPath: (path: Path) => SourceFile | undefined, ref: ReferencedFile): ReferenceFileLocation | SyntheticReferenceFileLocation {
const file = Debug.checkDefined(getSourceFileByPath(ref.file));
const { kind, index } = ref;
let pos: number | undefined, end: number | undefined, packageId: PackageId | undefined;
switch (kind) {
case FileIncludeKind.Import:
const importLiteral = getModuleNameStringLiteralAt(file, index);
packageId = file.resolvedModules?.get(importLiteral.text)?.packageId;
if (importLiteral.pos === -1) return { file, packageId, text: importLiteral.text };
pos = skipTrivia(file.text, importLiteral.pos);
end = importLiteral.end;
break;
case FileIncludeKind.ReferenceFile:
({ pos, end } = file.referencedFiles[index]);
break;
case FileIncludeKind.TypeReferenceDirective:
({ pos, end } = file.typeReferenceDirectives[index]);
packageId = file.resolvedTypeReferenceDirectiveNames?.get(toFileNameLowerCase(file.typeReferenceDirectives[index].fileName))?.packageId;
break;
case FileIncludeKind.LibReferenceDirective:
({ pos, end } = file.libReferenceDirectives[index]);
break;
default:
return Debug.assertNever(kind);
}
return { file, pos, end, packageId };
}
/**
@@ -754,14 +809,12 @@ namespace ts {
let noDiagnosticsTypeChecker: TypeChecker;
let classifiableNames: Set<__String>;
const ambientModuleNameToUnmodifiedFileName = new Map<string, string>();
// Todo:: Use this to report why file was included in --extendedDiagnostics
let refFileMap: MultiMap<Path, ts.RefFile> | undefined;
let fileReasons = createMultiMap<Path, FileIncludeReason>();
const cachedBindAndCheckDiagnosticsForFile: DiagnosticCache<Diagnostic> = {};
const cachedDeclarationDiagnosticsForFile: DiagnosticCache<DiagnosticWithLocation> = {};
let resolvedTypeReferenceDirectives = new Map<string, ResolvedTypeReferenceDirective | undefined>();
let fileProcessingDiagnostics = createDiagnosticCollection();
let fileProcessingDiagnostics: FilePreprocessingDiagnostics[] | undefined;
// The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
// This works as imported modules are discovered recursively in a depth first manner, specifically:
@@ -796,7 +849,7 @@ namespace ts {
// Map storing if there is emit blocking diagnostics for given input
const hasEmitBlockingDiagnostics = new Map<string, boolean>();
let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression | null | undefined;
let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression | false | undefined;
let moduleResolutionCache: ModuleResolutionCache | undefined;
let actualResolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[];
@@ -884,40 +937,35 @@ namespace ts {
resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
}
if (rootNames.length) {
for (const parsedRef of resolvedProjectReferences) {
if (!parsedRef) continue;
resolvedProjectReferences?.forEach((parsedRef, index) => {
if (!parsedRef) return;
const out = outFile(parsedRef.commandLine.options);
if (useSourceOfProjectReferenceRedirect) {
if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
for (const fileName of parsedRef.commandLine.fileNames) {
processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
processProjectReferenceFile(fileName, { kind: FileIncludeKind.SourceFromProjectReference, index });
}
}
}
else {
if (out) {
processSourceFile(changeExtension(out, ".d.ts"), /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
processProjectReferenceFile(changeExtension(out, ".d.ts"), { kind: FileIncludeKind.OutputFromProjectReference, index });
}
else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(parsedRef.commandLine, !host.useCaseSensitiveFileNames()));
for (const fileName of parsedRef.commandLine.fileNames) {
if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) {
processSourceFile(
getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory),
/*isDefaultLib*/ false,
/*ignoreNoDefaultLib*/ false,
/*packageId*/ undefined
);
processProjectReferenceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory), { kind: FileIncludeKind.OutputFromProjectReference, index });
}
}
}
}
}
});
}
}
tracing.push(tracing.Phase.Program, "processRootFiles", { count: rootNames.length });
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false));
forEach(rootNames, (name, index) => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.RootFile, index }));
tracing.pop();
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
@@ -930,7 +978,7 @@ namespace ts {
const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile);
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
for (let i = 0; i < typeReferences.length; i++) {
processTypeReferenceDirective(typeReferences[i], resolutions[i]);
processTypeReferenceDirective(typeReferences[i], resolutions[i], { kind: FileIncludeKind.AutomaticTypeDirectiveFile, typeReference: typeReferences[i], packageId: resolutions[i]?.packageId });
}
tracing.pop();
}
@@ -944,11 +992,11 @@ namespace ts {
// otherwise, using options specified in '--lib' instead of '--target' default library file
const defaultLibraryFileName = getDefaultLibraryFileName();
if (!options.lib && defaultLibraryFileName) {
processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false);
processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile });
}
else {
forEach(options.lib, libFileName => {
processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false);
forEach(options.lib, (libFileName, index) => {
processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile, index });
});
}
}
@@ -989,7 +1037,6 @@ namespace ts {
getSourceFileByPath,
getSourceFiles: () => files,
getMissingFilePaths: () => missingFilePaths!, // TODO: GH#18217
getRefFileMap: () => refFileMap,
getFilesByNameMap: () => filesByName,
getCompilerOptions: () => options,
getSyntacticDiagnostics,
@@ -1039,10 +1086,25 @@ namespace ts {
getSymlinkCache,
realpath: host.realpath?.bind(host),
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
getFileIncludeReasons: () => fileReasons,
structureIsReused,
};
onProgramCreateComplete();
// Add file processingDiagnostics
fileProcessingDiagnostics?.forEach(diagnostic => {
switch (diagnostic.kind) {
case FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic:
return programDiagnostics.add(createDiagnosticExplainingFile(diagnostic.file && getSourceFileByPath(diagnostic.file), diagnostic.fileProcessingReason, diagnostic.diagnostic, diagnostic.args || emptyArray));
case FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic:
const { file, pos, end } = getReferencedFileLocation(getSourceFileByPath, diagnostic.reason) as ReferenceFileLocation;
return programDiagnostics.add(createFileDiagnostic(file, Debug.checkDefined(pos), Debug.checkDefined(end) - pos, diagnostic.diagnostic, ...diagnostic.args || emptyArray));
default:
Debug.assertNever(diagnostic);
}
});
verifyCompilerOptions();
performance.mark("afterProgram");
performance.measure("Program", "beforeProgram", "afterProgram");
@@ -1521,7 +1583,6 @@ namespace ts {
}
missingFilePaths = oldProgram.getMissingFilePaths();
refFileMap = oldProgram.getRefFileMap();
// update fileName -> file mapping
Debug.assert(newSourceFiles.length === oldProgram.getSourceFiles().length);
@@ -1545,11 +1606,8 @@ namespace ts {
});
files = newSourceFiles;
fileReasons = oldProgram.getFileIncludeReasons();
fileProcessingDiagnostics = oldProgram.getFileProcessingDiagnostics();
for (const modifiedFile of modifiedSourceFiles) {
fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile);
}
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
@@ -1759,19 +1817,12 @@ namespace ts {
return emptyArray;
}
const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics.getDiagnostics(sourceFile.fileName);
const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName);
return getMergedProgramDiagnostics(sourceFile, fileProcessingDiagnosticsInFile, programDiagnosticsInFile);
}
function getMergedProgramDiagnostics(sourceFile: SourceFile, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) {
const flatDiagnostics = flatten(allDiagnostics);
if (!sourceFile.commentDirectives?.length) {
return flatDiagnostics;
return programDiagnosticsInFile;
}
return getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics).diagnostics;
return getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, programDiagnosticsInFile).diagnostics;
}
function getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] {
@@ -2153,11 +2204,8 @@ namespace ts {
function getOptionsDiagnostics(): SortedReadonlyArray<Diagnostic> {
return sortAndDeduplicateDiagnostics(concatenate(
fileProcessingDiagnostics.getGlobalDiagnostics(),
concatenate(
programDiagnostics.getGlobalDiagnostics(),
getOptionsDiagnosticsOfConfigFile()
)
programDiagnostics.getGlobalDiagnostics(),
getOptionsDiagnosticsOfConfigFile()
));
}
@@ -2178,8 +2226,8 @@ namespace ts {
return configFileParsingDiagnostics || emptyArray;
}
function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean) {
processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined);
function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason) {
processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined, reason);
}
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
@@ -2334,14 +2382,14 @@ namespace ts {
/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
function getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined {
return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), fileName => filesByName.get(toPath(fileName)) || undefined);
return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), getSourceFile);
}
function getSourceFileFromReferenceWorker(
fileName: string,
getSourceFile: (fileName: string) => SourceFile | undefined,
fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void,
refFile?: SourceFile): SourceFile | undefined {
reason?: FileIncludeReason): SourceFile | undefined {
if (hasExtension(fileName)) {
const canonicalFileName = host.getCanonicalFileName(fileName);
@@ -2368,7 +2416,7 @@ namespace ts {
fail(Diagnostics.File_0_not_found, fileName);
}
}
else if (refFile && canonicalFileName === host.getCanonicalFileName(refFile.fileName)) {
else if (isReferencedFile(reason) && canonicalFileName === host.getCanonicalFileName(getSourceFileByPath(reason.file)!.fileName)) {
fail(Diagnostics.A_file_cannot_have_a_reference_to_itself);
}
}
@@ -2390,34 +2438,27 @@ namespace ts {
}
/** This has side effects through `findSourceFile`. */
function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, refFile?: RefFile): void {
function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason): void {
getSourceFileFromReferenceWorker(
fileName,
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, refFile, packageId), // TODO: GH#18217
(diagnostic, ...args) => fileProcessingDiagnostics.add(
createRefFileDiagnostic(refFile, diagnostic, ...args)
),
refFile && refFile.file
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217
(diagnostic, ...args) => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, diagnostic, args),
reason
);
}
function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFile: SourceFile, refFile: RefFile | undefined): void {
const refs = !refFile ? refFileMap && refFileMap.get(existingFile.path) : undefined;
const refToReportErrorOn = refs && find(refs, ref => ref.referencedFileName === existingFile.fileName);
fileProcessingDiagnostics.add(refToReportErrorOn ?
createFileDiagnosticAtReference(
refToReportErrorOn,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
existingFile.fileName,
fileName,
) :
createRefFileDiagnostic(
refFile,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
fileName,
existingFile.fileName
)
);
function processProjectReferenceFile(fileName: string, reason: ProjectReferenceFile) {
return processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, reason);
}
function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFile: SourceFile, reason: FileIncludeReason): void {
const hasExistingReasonToReportErrorOn = !isReferencedFile(reason) && some(fileReasons.get(existingFile.path), isReferencedFile);
if (hasExistingReasonToReportErrorOn) {
addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing, [existingFile.fileName, fileName]);
}
else {
addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, [fileName, existingFile.fileName]);
}
}
function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path, resolvedPath: Path, originalFileName: string): SourceFile {
@@ -2442,18 +2483,18 @@ namespace ts {
}
// 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, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
tracing.push(tracing.Phase.Program, "findSourceFile", {
fileName,
isDefaultLib: isDefaultLib || undefined,
refKind: refFile ? (RefFileKind as any)[refFile.kind] : undefined,
fileIncludeKind: (FileIncludeKind as any)[reason.kind],
});
const result = findSourceFileWorker(fileName, path, isDefaultLib, ignoreNoDefaultLib, refFile, packageId);
const result = findSourceFileWorker(fileName, path, isDefaultLib, ignoreNoDefaultLib, reason, packageId);
tracing.pop();
return result;
}
function findSourceFileWorker(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile: RefFile | undefined, packageId: PackageId | undefined): SourceFile | undefined {
function findSourceFileWorker(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
if (useSourceOfProjectReferenceRedirect) {
let source = getSourceOfProjectReferenceRedirect(fileName);
// If preserveSymlinks is true, module resolution wont jump the symlink
@@ -2470,7 +2511,7 @@ namespace ts {
}
if (source) {
const file = isString(source) ?
findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, refFile, packageId) :
findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, reason, packageId) :
undefined;
if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined);
return file;
@@ -2479,7 +2520,7 @@ namespace ts {
const originalFileName = fileName;
if (filesByName.has(path)) {
const file = filesByName.get(path);
addFileToRefFileMap(fileName, file || undefined, refFile);
addFileIncludeReason(file || undefined, reason);
// try to check if we've already seen this file but with a different casing in path
// NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected
if (file && options.forceConsistentCasingInFileNames) {
@@ -2492,7 +2533,7 @@ namespace ts {
const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory);
const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(fileName, currentDirectory);
if (checkedAbsolutePath !== inputAbsolutePath) {
reportFileNamesDifferOnlyInCasingError(fileName, file, refFile);
reportFileNamesDifferOnlyInCasingError(fileName, file, reason);
}
}
@@ -2523,7 +2564,7 @@ namespace ts {
}
let redirectedPath: Path | undefined;
if (refFile && !useSourceOfProjectReferenceRedirect) {
if (isReferencedFile(reason) && !useSourceOfProjectReferenceRedirect) {
const redirectProject = getProjectReferenceRedirectProject(fileName);
if (redirectProject) {
if (outFile(redirectProject.commandLine.options)) {
@@ -2545,12 +2586,7 @@ namespace ts {
const file = host.getSourceFile(
fileName,
options.target!,
hostErrorMessage => fileProcessingDiagnostics.add(createRefFileDiagnostic(
refFile,
Diagnostics.Cannot_read_file_0_Colon_1,
fileName,
hostErrorMessage
)),
hostErrorMessage => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_read_file_0_Colon_1, [fileName, hostErrorMessage]),
shouldCreateNewSourceFile
);
@@ -2563,6 +2599,7 @@ namespace ts {
const dupFile = createRedirectSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName); // TODO: GH#18217
redirectTargetsMap.add(fileFromPackageId.path, fileName);
addFileToFilesByName(dupFile, path, redirectedPath);
addFileIncludeReason(dupFile, reason);
sourceFileToPackageName.set(path, packageId.name);
processingOtherFiles!.push(dupFile);
return dupFile;
@@ -2581,14 +2618,14 @@ namespace ts {
file.path = path;
file.resolvedPath = toPath(fileName);
file.originalFileName = originalFileName;
addFileToRefFileMap(fileName, file, refFile);
addFileIncludeReason(file, reason);
if (host.useCaseSensitiveFileNames()) {
const pathLowerCase = toFileNameLowerCase(path);
// for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
const existingFile = filesByNameIgnoreCase!.get(pathLowerCase);
if (existingFile) {
reportFileNamesDifferOnlyInCasingError(fileName, existingFile, refFile);
reportFileNamesDifferOnlyInCasingError(fileName, existingFile, reason);
}
else {
filesByNameIgnoreCase!.set(pathLowerCase, file);
@@ -2619,15 +2656,8 @@ namespace ts {
return file;
}
function addFileToRefFileMap(referencedFileName: string, file: SourceFile | undefined, refFile: RefFile | undefined) {
if (refFile && file) {
(refFileMap || (refFileMap = createMultiMap())).add(file.path, {
referencedFileName,
kind: refFile.kind,
index: refFile.index,
file: refFile.file.path
});
}
function addFileIncludeReason(file: SourceFile | undefined, reason: FileIncludeReason) {
if (file) fileReasons.add(file.path, reason);
}
function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) {
@@ -2728,19 +2758,12 @@ namespace ts {
function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
forEach(file.referencedFiles, (ref, index) => {
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
processSourceFile(
referencedFileName,
resolveTripleslashReference(ref.fileName, file.fileName),
isDefaultLib,
/*ignoreNoDefaultLib*/ false,
/*packageId*/ undefined,
{
kind: RefFileKind.ReferenceFile,
index,
file,
pos: ref.pos,
end: ref.end
}
{ kind: FileIncludeKind.ReferenceFile, file: file.path, index, }
);
});
}
@@ -2753,41 +2776,30 @@ namespace ts {
}
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file);
for (let i = 0; i < typeDirectives.length; i++) {
const ref = file.typeReferenceDirectives[i];
const resolvedTypeReferenceDirective = resolutions[i];
for (let index = 0; index < typeDirectives.length; index++) {
const ref = file.typeReferenceDirectives[index];
const resolvedTypeReferenceDirective = resolutions[index];
// store resolved type directive on the file
const fileName = toFileNameLowerCase(ref.fileName);
setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective);
processTypeReferenceDirective(
fileName,
resolvedTypeReferenceDirective,
{
kind: RefFileKind.TypeReferenceDirective,
index: i,
file,
pos: ref.pos,
end: ref.end
}
);
processTypeReferenceDirective(fileName, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, });
}
}
function processTypeReferenceDirective(
typeReferenceDirective: string,
resolvedTypeReferenceDirective?: ResolvedTypeReferenceDirective,
refFile?: RefFile
resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
reason: FileIncludeReason
): void {
tracing.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolveModuleNamesReusingOldState, refKind: refFile?.kind, refPath: refFile?.file.path });
processTypeReferenceDirectiveWorker(typeReferenceDirective, resolvedTypeReferenceDirective, refFile);
tracing.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolveModuleNamesReusingOldState, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined });
processTypeReferenceDirectiveWorker(typeReferenceDirective, resolvedTypeReferenceDirective, reason);
tracing.pop();
}
function processTypeReferenceDirectiveWorker(
typeReferenceDirective: string,
resolvedTypeReferenceDirective?: ResolvedTypeReferenceDirective,
refFile?: RefFile
resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
reason: FileIncludeReason
): void {
// If we already found this library as a primary reference - nothing to do
@@ -2801,7 +2813,7 @@ namespace ts {
if (resolvedTypeReferenceDirective.primary) {
// resolved from the primary path
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile); // TODO: GH#18217
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); // TODO: GH#18217
}
else {
// If we already resolved to this file, it must have been a secondary reference. Check file contents
@@ -2812,25 +2824,11 @@ namespace ts {
const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName!);
const existingFile = getSourceFile(previousResolution.resolvedFileName!)!;
if (otherFileText !== existingFile.text) {
// Try looking up ref for original file
const refs = !refFile ? refFileMap && refFileMap.get(existingFile.path) : undefined;
const refToReportErrorOn = refs && find(refs, ref => ref.referencedFileName === existingFile.fileName);
fileProcessingDiagnostics.add(
refToReportErrorOn ?
createFileDiagnosticAtReference(
refToReportErrorOn,
Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
typeReferenceDirective,
resolvedTypeReferenceDirective.resolvedFileName,
previousResolution.resolvedFileName
) :
createRefFileDiagnostic(
refFile,
Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
typeReferenceDirective,
resolvedTypeReferenceDirective.resolvedFileName,
previousResolution.resolvedFileName
)
addFilePreprocessingFileExplainingDiagnostic(
existingFile,
reason,
Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
[typeReferenceDirective, resolvedTypeReferenceDirective.resolvedFileName, previousResolution.resolvedFileName]
);
}
}
@@ -2839,18 +2837,14 @@ namespace ts {
}
else {
// First resolution of this library
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile);
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason);
}
}
if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--;
}
else {
fileProcessingDiagnostics.add(createRefFileDiagnostic(
refFile,
Diagnostics.Cannot_find_type_definition_file_for_0,
typeReferenceDirective
));
addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_find_type_definition_file_for_0, [typeReferenceDirective]);
}
if (saveResolution) {
@@ -2859,38 +2853,27 @@ namespace ts {
}
function processLibReferenceDirectives(file: SourceFile) {
forEach(file.libReferenceDirectives, libReference => {
forEach(file.libReferenceDirectives, (libReference, index) => {
const libName = toFileNameLowerCase(libReference.fileName);
const libFileName = libMap.get(libName);
if (libFileName) {
// we ignore any 'no-default-lib' reference set on this file.
processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true);
processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, });
}
else {
const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts");
const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity);
const message = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0;
fileProcessingDiagnostics.add(createFileDiagnostic(
file,
libReference.pos,
libReference.end - libReference.pos,
message,
libName,
suggestion
));
const diagnostic = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0;
(fileProcessingDiagnostics ||= []).push({
kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic,
reason: { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, },
diagnostic,
args: [libName, suggestion]
});
}
});
}
function createRefFileDiagnostic(refFile: RefFile | undefined, message: DiagnosticMessage, ...args: any[]): Diagnostic {
if (!refFile) {
return createCompilerDiagnostic(message, ...args);
}
else {
return createFileDiagnostic(refFile.file, refFile.pos, refFile.end - refFile.pos, message, ...args);
}
}
function getCanonicalFileName(fileName: string): string {
return host.getCanonicalFileName(fileName);
}
@@ -2902,9 +2885,9 @@ namespace ts {
const moduleNames = getModuleNames(file);
const resolutions = resolveModuleNamesReusingOldState(moduleNames, file);
Debug.assert(resolutions.length === moduleNames.length);
for (let i = 0; i < moduleNames.length; i++) {
const resolution = resolutions[i];
setResolvedModule(file, moduleNames[i], resolution);
for (let index = 0; index < moduleNames.length; index++) {
const resolution = resolutions[index];
setResolvedModule(file, moduleNames[index], resolution);
if (!resolution) {
continue;
@@ -2930,30 +2913,23 @@ namespace ts {
const shouldAddFile = resolvedFileName
&& !getResolutionDiagnostic(options, resolution)
&& !options.noResolve
&& i < file.imports.length
&& index < file.imports.length
&& !elideImport
&& !(isJsFile && !getAllowJSCompilerOption(options))
&& (isInJSFile(file.imports[i]) || !(file.imports[i].flags & NodeFlags.JSDoc));
&& (isInJSFile(file.imports[index]) || !(file.imports[index].flags & NodeFlags.JSDoc));
if (elideImport) {
modulesWithElidedImports.set(file.path, true);
}
else if (shouldAddFile) {
const path = toPath(resolvedFileName);
const pos = skipTrivia(file.text, file.imports[i].pos);
findSourceFile(
resolvedFileName,
path,
/*isDefaultLib*/ false,
/*ignoreNoDefaultLib*/ false,
{
kind: RefFileKind.Import,
index: i,
file,
pos,
end: file.imports[i].end
},
resolution.packageId
{ kind: FileIncludeKind.Import, file: file.path, index, },
resolution.packageId,
);
}
@@ -2971,19 +2947,14 @@ namespace ts {
function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean {
let allFilesBelongToPath = true;
const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
let rootPaths: Set<Path> | undefined;
for (const sourceFile of sourceFiles) {
if (!sourceFile.isDeclarationFile) {
const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
if (!rootPaths) rootPaths = new Set(rootNames.map(toPath));
addProgramDiagnosticAtRefPath(
addProgramDiagnosticExplainingFile(
sourceFile,
rootPaths,
Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
sourceFile.fileName,
rootDirectory
[sourceFile.fileName, rootDirectory]
);
allFilesBelongToPath = false;
}
@@ -3094,12 +3065,10 @@ namespace ts {
for (const file of files) {
// Ignore file that is not emitted
if (sourceFileMayBeEmitted(file, program) && !rootPaths.has(file.path)) {
addProgramDiagnosticAtRefPath(
addProgramDiagnosticExplainingFile(
file,
rootPaths,
Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
file.fileName,
options.configFilePath || ""
[file.fileName, options.configFilePath || ""]
);
}
}
@@ -3343,35 +3312,138 @@ namespace ts {
}
}
function createFileDiagnosticAtReference(refPathToReportErrorOn: ts.RefFile, message: DiagnosticMessage, ...args: (string | number | undefined)[]) {
const refFile = Debug.checkDefined(getSourceFileByPath(refPathToReportErrorOn.file));
const { kind, index } = refPathToReportErrorOn;
let pos: number, end: number;
switch (kind) {
case RefFileKind.Import:
pos = skipTrivia(refFile.text, refFile.imports[index].pos);
end = refFile.imports[index].end;
break;
case RefFileKind.ReferenceFile:
({ pos, end } = refFile.referencedFiles[index]);
break;
case RefFileKind.TypeReferenceDirective:
({ pos, end } = refFile.typeReferenceDirectives[index]);
break;
default:
return Debug.assertNever(kind);
function createDiagnosticExplainingFile(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason | undefined, diagnostic: DiagnosticMessage, args: (string | number | undefined)[] | undefined): Diagnostic {
let fileIncludeReasons: DiagnosticMessageChain[] | undefined;
let relatedInfo: Diagnostic[] | undefined;
let locationReason = isReferencedFile(fileProcessingReason) ? fileProcessingReason : undefined;
if (file) fileReasons.get(file.path)?.forEach(processReason);
if (fileProcessingReason) processReason(fileProcessingReason);
// If we have location and there is only one reason file is in which is the location, dont add details for file include
if (locationReason && fileIncludeReasons?.length === 1) fileIncludeReasons = undefined;
const location = locationReason && getReferencedFileLocation(getSourceFileByPath, locationReason);
const fileIncludeReasonDetails = fileIncludeReasons && chainDiagnosticMessages(fileIncludeReasons, Diagnostics.The_file_is_in_the_program_because_Colon);
const redirectInfo = file && explainIfFileIsRedirect(file);
const chain = chainDiagnosticMessages(redirectInfo ? fileIncludeReasonDetails ? [fileIncludeReasonDetails, ...redirectInfo] : redirectInfo : fileIncludeReasonDetails, diagnostic, ...args || emptyArray);
return location && isReferenceFileLocation(location) ?
createFileDiagnosticFromMessageChain(location.file, location.pos, location.end - location.pos, chain, relatedInfo) :
createCompilerDiagnosticFromMessageChain(chain, relatedInfo);
function processReason(reason: FileIncludeReason) {
(fileIncludeReasons ||= []).push(fileIncludeReasonToDiagnostics(program, reason));
if (!locationReason && isReferencedFile(reason)) {
// Report error at first reference file or file currently in processing and dont report in related information
locationReason = reason;
}
else if (locationReason !== reason) {
relatedInfo = append(relatedInfo, fileIncludeReasonToRelatedInformation(reason));
}
// Remove fileProcessingReason if its already included in fileReasons of the program
if (reason === fileProcessingReason) fileProcessingReason = undefined;
}
return createFileDiagnostic(refFile, pos, end - pos, message, ...args);
}
function addProgramDiagnosticAtRefPath(file: SourceFile, rootPaths: Set<Path>, message: DiagnosticMessage, ...args: (string | number | undefined)[]) {
const refPaths = refFileMap?.get(file.path);
const refPathToReportErrorOn = forEach(refPaths, refPath => rootPaths.has(refPath.file) ? refPath : undefined) ||
elementAt(refPaths, 0);
programDiagnostics.add(
refPathToReportErrorOn ?
createFileDiagnosticAtReference(refPathToReportErrorOn, message, ...args) :
createCompilerDiagnostic(message, ...args)
function addFilePreprocessingFileExplainingDiagnostic(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) {
(fileProcessingDiagnostics ||= []).push({
kind: FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic,
file: file && file.path,
fileProcessingReason,
diagnostic,
args
});
}
function addProgramDiagnosticExplainingFile(file: SourceFile, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) {
programDiagnostics.add(createDiagnosticExplainingFile(file, /*fileProcessingReason*/ undefined, diagnostic, args));
}
function fileIncludeReasonToRelatedInformation(reason: FileIncludeReason): DiagnosticWithLocation | undefined {
if (isReferencedFile(reason)) {
const referenceLocation = getReferencedFileLocation(getSourceFileByPath, reason);
let message: DiagnosticMessage;
switch (reason.kind) {
case FileIncludeKind.Import:
message = Diagnostics.File_is_included_via_import_here;
break;
case FileIncludeKind.ReferenceFile:
message = Diagnostics.File_is_included_via_reference_here;
break;
case FileIncludeKind.TypeReferenceDirective:
message = Diagnostics.File_is_included_via_type_library_reference_here;
break;
case FileIncludeKind.LibReferenceDirective:
message = Diagnostics.File_is_included_via_library_reference_here;
break;
default:
Debug.assertNever(reason);
}
return isReferenceFileLocation(referenceLocation) ? createFileDiagnostic(
referenceLocation.file,
referenceLocation.pos,
referenceLocation.end - referenceLocation.pos,
message,
) : undefined;
}
if (!options.configFile) return undefined;
let configFileNode: Node | undefined;
let message: DiagnosticMessage;
switch (reason.kind) {
case FileIncludeKind.RootFile:
if (!options.configFile.configFileSpecs) return undefined;
const fileName = getNormalizedAbsolutePath(rootNames[reason.index], currentDirectory);
const matchedByFiles = getMatchedFileSpec(program, fileName);
if (matchedByFiles) {
configFileNode = getTsConfigPropArrayElementValue(options.configFile, "files", matchedByFiles);
message = Diagnostics.File_is_matched_by_files_list_specified_here;
break;
}
const matchedByInclude = getMatchedIncludeSpec(program, fileName);
// Could be additional files specified as roots
if (!matchedByInclude) return undefined;
configFileNode = getTsConfigPropArrayElementValue(options.configFile, "include", matchedByInclude);
message = Diagnostics.File_is_matched_by_include_pattern_specified_here;
break;
case FileIncludeKind.SourceFromProjectReference:
case FileIncludeKind.OutputFromProjectReference:
const referencedResolvedRef = Debug.checkDefined(resolvedProjectReferences?.[reason.index]);
const referenceInfo = forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) =>
resolvedRef === referencedResolvedRef ? { sourceFile: parent?.sourceFile || options.configFile!, index } : undefined
);
if (!referenceInfo) return undefined;
const { sourceFile, index } = referenceInfo;
const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile as TsConfigSourceFile, "references"),
property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined);
return referencesSyntax && referencesSyntax.elements.length > index ?
createDiagnosticForNodeInSourceFile(
sourceFile,
referencesSyntax.elements[index],
reason.kind === FileIncludeKind.OutputFromProjectReference ?
Diagnostics.File_is_output_from_referenced_project_specified_here :
Diagnostics.File_is_source_from_referenced_project_specified_here,
) :
undefined;
case FileIncludeKind.AutomaticTypeDirectiveFile:
if (!options.types) return undefined;
configFileNode = getOptionsSyntaxByArrayElementValue("types", reason.typeReference);
message = Diagnostics.File_is_entry_point_of_type_library_specified_here;
break;
case FileIncludeKind.LibFile:
if (reason.index !== undefined) {
configFileNode = getOptionsSyntaxByArrayElementValue("lib", options.lib![reason.index]);
message = Diagnostics.File_is_library_specified_here;
break;
}
const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === options.target ? key : undefined);
configFileNode = target ? getOptionsSyntaxByValue("target", target) : undefined;
message = Diagnostics.File_is_default_library_for_target_specified_here;
break;
default:
Debug.assertNever(reason);
}
return configFileNode && createDiagnosticForNodeInSourceFile(
options.configFile,
configFileNode,
message,
);
}
@@ -3447,16 +3519,23 @@ namespace ts {
}
}
function getOptionsSyntaxByName(name: string): object | undefined {
function getOptionsSyntaxByName(name: string) {
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
if (compilerOptionsObjectLiteralSyntax) {
return getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name);
}
return undefined;
return compilerOptionsObjectLiteralSyntax && getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name);
}
function getOptionPathsSyntax(): PropertyAssignment[] {
return getOptionsSyntaxByName("paths") as PropertyAssignment[] || emptyArray;
function getOptionPathsSyntax() {
return getOptionsSyntaxByName("paths") || emptyArray;
}
function getOptionsSyntaxByValue(name: string, value: string) {
const syntaxByName = getOptionsSyntaxByName(name);
return syntaxByName && firstDefined(syntaxByName, property => isStringLiteral(property.initializer) && property.initializer.text === value ? property.initializer : undefined);
}
function getOptionsSyntaxByArrayElementValue(name: string, value: string) {
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
return compilerOptionsObjectLiteralSyntax && getPropertyArrayElementValue(compilerOptionsObjectLiteralSyntax, name, value);
}
function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string, option3?: string) {
@@ -3490,7 +3569,7 @@ namespace ts {
function getCompilerOptionsObjectLiteralSyntax() {
if (_compilerOptionsObjectLiteralSyntax === undefined) {
_compilerOptionsObjectLiteralSyntax = null; // eslint-disable-line no-null/no-null
_compilerOptionsObjectLiteralSyntax = false;
const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile);
if (jsonObjectLiteral) {
for (const prop of getPropertyAssignment(jsonObjectLiteral, "compilerOptions")) {
@@ -3501,7 +3580,7 @@ namespace ts {
}
}
}
return _compilerOptionsObjectLiteralSyntax;
return _compilerOptionsObjectLiteralSyntax || undefined;
}
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number): boolean {
@@ -3743,8 +3822,8 @@ namespace ts {
export const emitSkippedWithNoDiagnostics: EmitResult = { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
/*@internal*/
export function handleNoEmitOptions(
program: ProgramToEmitFilesAndReportErrors,
export function handleNoEmitOptions<T extends BuilderProgram>(
program: Program | T,
sourceFile: SourceFile | undefined,
writeFile: WriteFileCallback | undefined,
cancellationToken: CancellationToken | undefined
@@ -3893,4 +3972,17 @@ namespace ts {
}
return res;
}
function getModuleNameStringLiteralAt({ imports, moduleAugmentations }: SourceFile, index: number): StringLiteralLike {
if (index < imports.length) return imports[index];
let augIndex = imports.length;
for (const aug of moduleAugmentations) {
if (aug.kind === SyntaxKind.StringLiteral) {
if (index === augIndex) return aug;
augIndex++;
}
// Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
}
Debug.fail("should never ask for module name at index higher than possible module name");
}
}
+13 -15
View File
@@ -14,6 +14,7 @@ namespace ts {
/*@internal*/ preserveWatchOutput?: boolean;
/*@internal*/ listEmittedFiles?: boolean;
/*@internal*/ listFiles?: boolean;
/*@internal*/ explainFiles?: boolean;
/*@internal*/ pretty?: boolean;
incremental?: boolean;
assumeChangesOnlyAffectDirectDependencies?: boolean;
@@ -215,7 +216,7 @@ namespace ts {
readonly currentDirectory: string;
readonly getCanonicalFileName: GetCanonicalFileName;
readonly parseConfigFileHost: ParseConfigFileHost;
readonly writeFileName: ((s: string) => void) | undefined;
readonly write: ((s: string) => void) | undefined;
// State of solution
readonly options: BuildOptions;
@@ -287,7 +288,7 @@ namespace ts {
currentDirectory,
getCanonicalFileName,
parseConfigFileHost: parseConfigHostFromCompilerHostLike(host),
writeFileName: host.trace ? (s: string) => host.trace!(s) : undefined,
write: maybeBind(host, host.trace),
// State of solution
options,
@@ -892,7 +893,7 @@ namespace ts {
const { emitResult } = emitFilesAndReportErrors(
program,
reportDeclarationDiagnostics,
/*writeFileName*/ undefined,
/*write*/ undefined,
/*reportSummary*/ undefined,
(name, text, writeByteOrderMark) => outputFiles.push({ name, text, writeByteOrderMark }),
cancellationToken,
@@ -965,7 +966,7 @@ namespace ts {
buildResult = BuildResultFlags.EmitErrors & buildResult!;
}
if (emitResult.emittedFiles && state.writeFileName) {
if (emitResult.emittedFiles && state.write) {
emitResult.emittedFiles.forEach(name => listEmittedFile(state, config, name));
}
afterProgramDone(state, program, config);
@@ -995,7 +996,7 @@ namespace ts {
return emitDiagnostics;
}
if (state.writeFileName) {
if (state.write) {
emittedOutputs.forEach(name => listEmittedFile(state, config, name));
}
@@ -1169,9 +1170,8 @@ namespace ts {
}
else if (reloadLevel === ConfigFileProgramReloadLevel.Partial) {
// Update file names
const result = getFileNamesFromConfigSpecs(config.configFileSpecs!, getDirectoryPath(project), config.options, state.parseConfigFileHost);
updateErrorForNoInputFiles(result, project, config.configFileSpecs!, config.errors, canJsonReportNoInputFiles(config.raw));
config.fileNames = result.fileNames;
config.fileNames = getFileNamesFromConfigSpecs(config.options.configFile!.configFileSpecs!, getDirectoryPath(project), config.options, state.parseConfigFileHost);
updateErrorForNoInputFiles(config.fileNames, project, config.options.configFile!.configFileSpecs!, config.errors, canJsonReportNoInputFiles(config.raw));
watchInputFiles(state, project, projectPath, config);
}
@@ -1240,9 +1240,9 @@ namespace ts {
return undefined;
}
function listEmittedFile({ writeFileName }: SolutionBuilderState, proj: ParsedCommandLine, file: string) {
if (writeFileName && proj.options.listEmittedFiles) {
writeFileName(`TSFILE: ${file}`);
function listEmittedFile({ write }: SolutionBuilderState, proj: ParsedCommandLine, file: string) {
if (write && proj.options.listEmittedFiles) {
write(`TSFILE: ${file}`);
}
}
@@ -1259,7 +1259,7 @@ namespace ts {
config: ParsedCommandLine
) {
if (program) {
if (program && state.writeFileName) listFiles(program, state.writeFileName);
if (program && state.write) listFiles(program, state.write);
if (state.host.afterProgramEmitAndDiagnostics) {
state.host.afterProgramEmitAndDiagnostics(program);
}
@@ -1283,7 +1283,6 @@ namespace ts {
const canEmitBuildInfo = !(buildResult & BuildResultFlags.SyntaxErrors) && program && !outFile(program.getCompilerOptions());
reportAndStoreErrors(state, resolvedPath, diagnostics);
// List files if any other build error using program (emit errors already report files)
state.projectStatus.set(resolvedPath, { type: UpToDateStatusType.Unbuildable, reason: `${errorType} errors` });
if (canEmitBuildInfo) return { buildResult, step: BuildStep.EmitBuildInfo };
afterProgramDone(state, program, config);
@@ -1794,7 +1793,7 @@ namespace ts {
if (!state.watch) return;
updateWatchingWildcardDirectories(
getOrCreateValueMapFromConfigFileMap(state.allWatchedWildcardDirectories, resolvedPath),
new Map(getEntries(parsed.configFileSpecs!.wildcardDirectories)),
new Map(getEntries(parsed.wildcardDirectories!)),
(dir, flags) => state.watchDirectory(
dir,
fileOrDirectory => {
@@ -1803,7 +1802,6 @@ namespace ts {
fileOrDirectory,
fileOrDirectoryPath: toPath(state, fileOrDirectory),
configFileName: resolved,
configFileSpecs: parsed.configFileSpecs!,
currentDirectory: state.currentDirectory,
options: parsed.options,
program: state.builderPrograms.get(resolvedPath),
+85 -19
View File
@@ -3620,6 +3620,7 @@ namespace ts {
export interface TsConfigSourceFile extends JsonSourceFile {
extendedSourceFiles?: string[];
/*@internal*/ configFileSpecs?: ConfigFileSpecs;
}
export interface JsonMinusNumericLiteral extends PrefixUnaryExpression {
@@ -3689,20 +3690,94 @@ namespace ts {
}
/*@internal*/
export enum RefFileKind {
export enum FileIncludeKind {
RootFile,
SourceFromProjectReference,
OutputFromProjectReference,
Import,
ReferenceFile,
TypeReferenceDirective
TypeReferenceDirective,
LibFile,
LibReferenceDirective,
AutomaticTypeDirectiveFile
}
/*@internal*/
export interface RefFile {
referencedFileName: string;
kind: RefFileKind;
export interface RootFile {
kind: FileIncludeKind.RootFile,
index: number;
file: Path;
}
/*@internal*/
export interface LibFile {
kind: FileIncludeKind.LibFile;
index?: number;
}
/*@internal*/
export type ProjectReferenceFileKind = FileIncludeKind.SourceFromProjectReference |
FileIncludeKind.OutputFromProjectReference;
/*@internal*/
export interface ProjectReferenceFile {
kind: ProjectReferenceFileKind;
index: number;
}
/*@internal*/
export type ReferencedFileKind = FileIncludeKind.Import |
FileIncludeKind.ReferenceFile |
FileIncludeKind.TypeReferenceDirective |
FileIncludeKind.LibReferenceDirective;
/*@internal*/
export interface ReferencedFile {
kind: ReferencedFileKind;
file: Path;
index: number;
}
/*@internal*/
export interface AutomaticTypeDirectiveFile {
kind: FileIncludeKind.AutomaticTypeDirectiveFile;
typeReference: string;
packageId: PackageId | undefined;
}
/*@internal*/
export type FileIncludeReason =
RootFile |
LibFile |
ProjectReferenceFile |
ReferencedFile |
AutomaticTypeDirectiveFile;
/*@internal*/
export const enum FilePreprocessingDiagnosticsKind {
FilePreprocessingReferencedDiagnostic,
FilePreprocessingFileExplainingDiagnostic
}
/*@internal*/
export interface FilePreprocessingReferencedDiagnostic {
kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic;
reason: ReferencedFile;
diagnostic: DiagnosticMessage;
args?: (string | number | undefined)[];
}
/*@internal*/
export interface FilePreprocessingFileExplainingDiagnostic {
kind: FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic;
file?: Path;
fileProcessingReason: FileIncludeReason;
diagnostic: DiagnosticMessage;
args?: (string | number | undefined)[];
}
/*@internal*/
export type FilePreprocessingDiagnostics = FilePreprocessingReferencedDiagnostic | FilePreprocessingFileExplainingDiagnostic;
export interface Program extends ScriptReferenceHost {
getCurrentDirectory(): string;
/**
@@ -3722,8 +3797,6 @@ namespace ts {
/* @internal */
getMissingFilePaths(): readonly Path[];
/* @internal */
getRefFileMap(): MultiMap<Path, RefFile> | undefined;
/* @internal */
getFilesByNameMap(): ESMap<string, SourceFile | false | undefined>;
/**
@@ -3777,7 +3850,7 @@ namespace ts {
getInstantiationCount(): number;
getRelationCacheSizes(): { assignable: number, identity: number, subtype: number, strictSubtype: number };
/* @internal */ getFileProcessingDiagnostics(): DiagnosticCollection;
/* @internal */ getFileProcessingDiagnostics(): FilePreprocessingDiagnostics[] | undefined;
/* @internal */ getResolvedTypeReferenceDirectives(): ESMap<string, ResolvedTypeReferenceDirective | undefined>;
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
isSourceFileDefaultLibrary(file: SourceFile): boolean;
@@ -3795,6 +3868,8 @@ namespace ts {
/* @internal */ redirectTargetsMap: MultiMap<string, string>;
/** Is the file emitted file */
/* @internal */ isEmittedFile(file: string): boolean;
/* @internal */ getFileIncludeReasons(): MultiMap<Path, FileIncludeReason>;
/* @internal */ useCaseSensitiveFileNames(): boolean;
/* @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined;
@@ -5769,6 +5844,7 @@ namespace ts {
lib?: string[];
/*@internal*/listEmittedFiles?: boolean;
/*@internal*/listFiles?: boolean;
/*@internal*/explainFiles?: boolean;
/*@internal*/listFilesOnly?: boolean;
locale?: string;
mapRoot?: string;
@@ -5957,7 +6033,6 @@ namespace ts {
errors: Diagnostic[];
wildcardDirectories?: MapLike<WatchDirectoryFlags>;
compileOnSave?: boolean;
/* @internal */ configFileSpecs?: ConfigFileSpecs;
}
export const enum WatchDirectoryFlags {
@@ -5979,13 +6054,6 @@ namespace ts {
validatedFilesSpec: readonly string[] | undefined;
validatedIncludeSpecs: readonly string[] | undefined;
validatedExcludeSpecs: readonly string[] | undefined;
wildcardDirectories: MapLike<WatchDirectoryFlags>;
}
export interface ExpandResult {
fileNames: string[];
wildcardDirectories: MapLike<WatchDirectoryFlags>;
/* @internal */ spec: ConfigFileSpecs;
}
/* @internal */
@@ -7914,8 +7982,6 @@ namespace ts {
// Otherwise, returns all the diagnostics (global and file associated) in this collection.
getDiagnostics(): Diagnostic[];
getDiagnostics(fileName: string): DiagnosticWithLocation[];
reattachFileDiagnostics(newFile: SourceFile): void;
}
// SyntaxKind.SyntaxList
+34 -19
View File
@@ -988,10 +988,25 @@ namespace ts {
export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation {
const sourceFile = getSourceFileOfNode(node);
const span = getErrorSpanForNode(sourceFile, node);
return createFileDiagnosticFromMessageChain(sourceFile, span.start, span.length, messageChain, relatedInformation);
}
function assertDiagnosticLocation(file: SourceFile | undefined, start: number, length: number) {
Debug.assertGreaterThanOrEqual(start, 0);
Debug.assertGreaterThanOrEqual(length, 0);
if (file) {
Debug.assertLessThanOrEqual(start, file.text.length);
Debug.assertLessThanOrEqual(start + length, file.text.length);
}
}
export function createFileDiagnosticFromMessageChain(file: SourceFile, start: number, length: number, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation {
assertDiagnosticLocation(file, start, length);
return {
file: sourceFile,
start: span.start,
length: span.length,
file,
start,
length,
code: messageChain.code,
category: messageChain.category,
messageText: messageChain.next ? messageChain : messageChain.messageText,
@@ -1496,6 +1511,13 @@ namespace ts {
});
}
export function getPropertyArrayElementValue(objectLiteral: ObjectLiteralExpression, propKey: string, elementValue: string): StringLiteral | undefined {
return firstDefined(getPropertyAssignment(objectLiteral, propKey), property =>
isArrayLiteralExpression(property.initializer) ?
find(property.initializer.elements, (element): element is StringLiteral => isStringLiteral(element) && element.text === elementValue) :
undefined);
}
export function getTsConfigObjectLiteralExpression(tsConfigSourceFile: TsConfigSourceFile | undefined): ObjectLiteralExpression | undefined {
if (tsConfigSourceFile && tsConfigSourceFile.statements.length) {
const expression = tsConfigSourceFile.statements[0].expression;
@@ -3667,13 +3689,8 @@ namespace ts {
lookup,
getGlobalDiagnostics,
getDiagnostics,
reattachFileDiagnostics
};
function reattachFileDiagnostics(newFile: SourceFile): void {
forEach(fileDiagnostics.get(newFile.fileName), diagnostic => diagnostic.file = newFile);
}
function lookup(diagnostic: Diagnostic): Diagnostic | undefined {
let diagnostics: SortedArray<Diagnostic> | undefined;
if (diagnostic.file) {
@@ -5693,9 +5710,7 @@ namespace ts {
export function createDetachedDiagnostic(fileName: string, start: number, length: number, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticWithDetachedLocation;
export function createDetachedDiagnostic(fileName: string, start: number, length: number, message: DiagnosticMessage): DiagnosticWithDetachedLocation {
Debug.assertGreaterThanOrEqual(start, 0);
Debug.assertGreaterThanOrEqual(length, 0);
assertDiagnosticLocation(/*file*/ undefined, start, length);
let text = getLocaleSpecificMessage(message);
if (arguments.length > 4) {
@@ -5763,13 +5778,7 @@ namespace ts {
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticWithLocation;
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): DiagnosticWithLocation {
Debug.assertGreaterThanOrEqual(start, 0);
Debug.assertGreaterThanOrEqual(length, 0);
if (file) {
Debug.assertLessThanOrEqual(start, file.text.length);
Debug.assertLessThanOrEqual(start + length, file.text.length);
}
assertDiagnosticLocation(file, start, length);
let text = getLocaleSpecificMessage(message);
@@ -5822,7 +5831,7 @@ namespace ts {
};
}
export function createCompilerDiagnosticFromMessageChain(chain: DiagnosticMessageChain): Diagnostic {
export function createCompilerDiagnosticFromMessageChain(chain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): Diagnostic {
return {
file: undefined,
start: undefined,
@@ -5831,6 +5840,7 @@ namespace ts {
code: chain.code,
category: chain.category,
messageText: chain.next ? chain : chain.messageText,
relatedInformation
};
}
@@ -6222,6 +6232,11 @@ namespace ts {
return !/[.*?]/.test(lastPathComponent);
}
export function getPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude") {
const pattern = spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]);
return pattern && `^(${pattern})${usage === "exclude" ? "($|/)" : "$"}`;
}
function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined {
let subpattern = "";
let hasWrittenComponent = false;
+189 -30
View File
@@ -113,38 +113,197 @@ namespace ts {
return `${newLine}${flattenDiagnosticMessageText(d.messageText, newLine)}${newLine}${newLine}`;
}
/**
* Program structure needed to emit the files and report diagnostics
*/
export interface ProgramToEmitFilesAndReportErrors {
getCurrentDirectory(): string;
getCompilerOptions(): CompilerOptions;
getSourceFiles(): readonly SourceFile[];
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[];
getConfigFileParsingDiagnostics(): readonly Diagnostic[];
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
export function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T {
return !!(program as T).getState;
}
export function listFiles(program: ProgramToEmitFilesAndReportErrors, writeFileName: (s: string) => void) {
if (program.getCompilerOptions().listFiles || program.getCompilerOptions().listFilesOnly) {
export 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 => {
writeFileName(file.fileName);
write(file.fileName);
});
}
}
export function explainFiles(program: Program, write: (s: string) => void) {
const reasons = program.getFileIncludeReasons();
const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames());
const relativeFileName = (fileName: string) => convertToRelativePath(fileName, program.getCurrentDirectory(), getCanonicalFileName);
for (const file of program.getSourceFiles()) {
write(`${toFileName(file, relativeFileName)}`);
reasons.get(file.path)?.forEach(reason => write(` ${fileIncludeReasonToDiagnostics(program, reason, relativeFileName).messageText}`));
explainIfFileIsRedirect(file, relativeFileName)?.forEach(d => write(` ${d.messageText}`));
}
}
export function explainIfFileIsRedirect(file: SourceFile, 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)
));
}
return result;
}
export function getMatchedFileSpec(program: Program, fileName: string) {
const configFile = program.getCompilerOptions().configFile;
if (!configFile?.configFileSpecs?.validatedFilesSpec) return undefined;
const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames());
const filePath = getCanonicalFileName(fileName);
const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory()));
return find(configFile.configFileSpecs.validatedFilesSpec, fileSpec => getCanonicalFileName(getNormalizedAbsolutePath(fileSpec, basePath)) === filePath);
}
export function getMatchedIncludeSpec(program: Program, fileName: string) {
const configFile = program.getCompilerOptions().configFile;
if (!configFile?.configFileSpecs?.validatedIncludeSpecs) return undefined;
const isJsonFile = fileExtensionIs(fileName, Extension.Json);
const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory()));
const useCaseSensitiveFileNames = program.useCaseSensitiveFileNames();
return find(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);
});
}
export function fileIncludeReasonToDiagnostics(program: Program, reason: FileIncludeReason, fileNameConvertor?: (fileName: string) => string,): DiagnosticMessageChain {
const options = program.getCompilerOptions();
if (isReferencedFile(reason)) {
const referenceLocation = getReferencedFileLocation(path => program.getSourceFileByPath(path), 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 matchedByInclude ?
chainDiagnosticMessages(
/*details*/ undefined,
Diagnostics.Matched_by_include_pattern_0_in_1,
matchedByInclude,
toFileName(options.configFile, fileNameConvertor)
) :
// Could be additional files specified as roots
chainDiagnosticMessages(/*details*/ undefined, 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,
outFile(options) ?
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:
return chainDiagnosticMessages(
/*details*/ undefined,
options.types ?
reason.packageId ?
Diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions_with_packageId_1 :
Diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions :
reason.packageId ?
Diagnostics.Entry_point_for_implicit_type_library_0_with_packageId_1 :
Diagnostics.Entry_point_for_implicit_type_library_0,
reason.typeReference,
reason.packageId && packageIdToString(reason.packageId),
);
case FileIncludeKind.LibFile:
if (reason.index !== undefined) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Library_0_specified_in_compilerOptions, options.lib![reason.index]);
const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === options.target ? key : undefined);
return chainDiagnosticMessages(
/*details*/ undefined,
target ?
Diagnostics.Default_library_for_target_0 :
Diagnostics.Default_library,
target,
);
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
*/
export function emitFilesAndReportErrors(
program: ProgramToEmitFilesAndReportErrors,
export function emitFilesAndReportErrors<T extends BuilderProgram>(
program: Program | T,
reportDiagnostic: DiagnosticReporter,
writeFileName?: (s: string) => void,
write?: (s: string) => void,
reportSummary?: ReportEmitErrorSummary,
writeFile?: WriteFileCallback,
cancellationToken?: CancellationToken,
@@ -181,13 +340,13 @@ namespace ts {
const diagnostics = sortAndDeduplicateDiagnostics(allDiagnostics);
diagnostics.forEach(reportDiagnostic);
if (writeFileName) {
if (write) {
const currentDir = program.getCurrentDirectory();
forEach(emittedFiles, file => {
const filepath = getNormalizedAbsolutePath(file, currentDir);
writeFileName(`TSFILE: ${filepath}`);
write(`TSFILE: ${filepath}`);
});
listFiles(program, writeFileName);
listFiles(program, write);
}
if (reportSummary) {
@@ -200,10 +359,10 @@ namespace ts {
};
}
export function emitFilesAndReportErrorsAndGetExitStatus(
program: ProgramToEmitFilesAndReportErrors,
export function emitFilesAndReportErrorsAndGetExitStatus<T extends BuilderProgram>(
program: Program | T,
reportDiagnostic: DiagnosticReporter,
writeFileName?: (s: string) => void,
write?: (s: string) => void,
reportSummary?: ReportEmitErrorSummary,
writeFile?: WriteFileCallback,
cancellationToken?: CancellationToken,
@@ -213,7 +372,7 @@ namespace ts {
const { emitResult, diagnostics } = emitFilesAndReportErrors(
program,
reportDiagnostic,
writeFileName,
write,
reportSummary,
writeFile,
cancellationToken,
@@ -385,7 +544,7 @@ namespace ts {
* 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 writeFileName = (s: string) => system.write(s + system.newLine);
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 => {
@@ -395,7 +554,7 @@ namespace ts {
emitFilesAndReportErrors(
builderProgram,
reportDiagnostic,
writeFileName,
write,
errorCount => result.onWatchStatusChange!(
createCompilerDiagnostic(getWatchErrorSummaryDiagnosticMessage(errorCount), errorCount),
newLine,
+6 -8
View File
@@ -260,7 +260,7 @@ namespace ts {
const currentDirectory = host.getCurrentDirectory();
const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {}, watchOptionsToExtend, extraFileExtensions, createProgram } = host;
let { rootFiles: rootFileNames, options: compilerOptions, watchOptions, projectReferences } = host;
let configFileSpecs: ConfigFileSpecs;
let wildcardDirectories: MapLike<WatchDirectoryFlags> | undefined;
let configFileParsingDiagnostics: Diagnostic[] | undefined;
let canConfigFileJsonReportNoInputFiles = false;
let hasChangedConfigFileParsingErrors = false;
@@ -635,11 +635,10 @@ namespace ts {
function reloadFileNamesFromConfigFile() {
writeLog("Reloading new file names and options");
const result = getFileNamesFromConfigSpecs(configFileSpecs, getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), compilerOptions, parseConfigFileHost, extraFileExtensions);
if (updateErrorForNoInputFiles(result, getNormalizedAbsolutePath(configFileName, currentDirectory), configFileSpecs, configFileParsingDiagnostics!, canConfigFileJsonReportNoInputFiles)) {
rootFileNames = getFileNamesFromConfigSpecs(compilerOptions.configFile!.configFileSpecs!, getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), compilerOptions, parseConfigFileHost, extraFileExtensions);
if (updateErrorForNoInputFiles(rootFileNames, getNormalizedAbsolutePath(configFileName, currentDirectory), compilerOptions.configFile!.configFileSpecs!, configFileParsingDiagnostics!, canConfigFileJsonReportNoInputFiles)) {
hasChangedConfigFileParsingErrors = true;
}
rootFileNames = result.fileNames;
// Update the program
synchronizeProgram();
@@ -668,8 +667,8 @@ namespace ts {
rootFileNames = configFileParseResult.fileNames;
compilerOptions = configFileParseResult.options;
watchOptions = configFileParseResult.watchOptions;
configFileSpecs = configFileParseResult.configFileSpecs!; // TODO: GH#18217
projectReferences = configFileParseResult.projectReferences;
wildcardDirectories = configFileParseResult.wildcardDirectories;
configFileParsingDiagnostics = getConfigFileParsingDiagnostics(configFileParseResult).slice();
canConfigFileJsonReportNoInputFiles = canJsonReportNoInputFiles(configFileParseResult.raw);
hasChangedConfigFileParsingErrors = true;
@@ -726,10 +725,10 @@ namespace ts {
}
function watchConfigFileWildCardDirectories() {
if (configFileSpecs) {
if (wildcardDirectories) {
updateWatchingWildcardDirectories(
watchedWildcardDirectories || (watchedWildcardDirectories = new Map()),
new Map(getEntries(configFileSpecs.wildcardDirectories)),
new Map(getEntries(wildcardDirectories)),
watchWildcardDirectory
);
}
@@ -757,7 +756,6 @@ namespace ts {
fileOrDirectory,
fileOrDirectoryPath,
configFileName,
configFileSpecs,
extraFileExtensions,
options: compilerOptions,
program: getCurrentBuilderProgram(),
+2 -3
View File
@@ -336,7 +336,6 @@ namespace ts {
fileOrDirectoryPath: Path;
configFileName: string;
options: CompilerOptions;
configFileSpecs: ConfigFileSpecs;
program: BuilderProgram | Program | undefined;
extraFileExtensions?: readonly FileExtensionInfo[];
currentDirectory: string;
@@ -346,7 +345,7 @@ namespace ts {
/* @internal */
export function isIgnoredFileFromWildCardWatching({
watchedDirPath, fileOrDirectory, fileOrDirectoryPath,
configFileName, options, configFileSpecs, program, extraFileExtensions,
configFileName, options, program, extraFileExtensions,
currentDirectory, useCaseSensitiveFileNames,
writeLog,
}: IsIgnoredFileFromWildCardWatchingInput): boolean {
@@ -366,7 +365,7 @@ namespace ts {
return true;
}
if (isExcludedFile(fileOrDirectory, configFileSpecs, getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), useCaseSensitiveFileNames, currentDirectory)) {
if (isExcludedFile(fileOrDirectory, options.configFile!.configFileSpecs!, getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory), useCaseSensitiveFileNames, currentDirectory)) {
writeLog(`Project: ${configFileName} Detected excluded file: ${fileOrDirectory}`);
return true;
}
+8 -2
View File
@@ -271,6 +271,7 @@ namespace FourSlash {
}
}
let configParseResult: ts.ParsedCommandLine | undefined;
if (configFileName) {
const baseDir = ts.normalizePath(ts.getDirectoryPath(configFileName));
const files: vfs.FileSet = { [baseDir]: {} };
@@ -281,7 +282,8 @@ namespace FourSlash {
const fs = new vfs.FileSystem(/*ignoreCase*/ true, { cwd: baseDir, files });
const host = new fakes.ParseConfigHost(fs);
const jsonSourceFile = ts.parseJsonText(configFileName, this.inputFiles.get(configFileName)!);
compilationOptions = ts.parseJsonSourceFileConfigFileContent(jsonSourceFile, host, baseDir, compilationOptions, configFileName).options;
configParseResult = ts.parseJsonSourceFileConfigFileContent(jsonSourceFile, host, baseDir, compilationOptions, configFileName);
compilationOptions = configParseResult.options;
}
if (compilationOptions.typeRoots) {
@@ -336,7 +338,11 @@ namespace FourSlash {
// resolveReference file-option is not specified then do not resolve any files and include all inputFiles
this.inputFiles.forEach((file, fileName) => {
if (!Harness.isDefaultLibraryFile(fileName)) {
this.languageServiceAdapterHost.addScript(fileName, file, /*isRootFile*/ true);
// all files if config file not specified, otherwise root files from the config and typings cache files are root files
const isRootFile = !configParseResult ||
ts.contains(configParseResult.fileNames, fileName) ||
(ts.isDeclarationFileName(fileName) && ts.containsPath("/Library/Caches/typescript", fileName));
this.languageServiceAdapterHost.addScript(fileName, file, isRootFile);
}
});
@@ -1012,6 +1012,10 @@ interface Array<T> { length: number; [n: number]: T; }`
}
}
prependFile(path: string, content: string, options?: Partial<ReloadWatchInvokeOptions>): void {
this.modifyFile(path, content + this.readFile(path), options);
}
appendFile(path: string, content: string, options?: Partial<ReloadWatchInvokeOptions>): void {
this.modifyFile(path, this.readFile(path) + content, options);
}
+4 -6
View File
@@ -1279,7 +1279,6 @@ namespace ts.server {
fileOrDirectory,
fileOrDirectoryPath,
configFileName,
configFileSpecs: project.configFileSpecs!,
extraFileExtensions: this.hostConfiguration.extraFileExtensions,
currentDirectory: this.currentDirectory,
options: project.getCompilationSettings(),
@@ -2128,7 +2127,6 @@ namespace ts.server {
configHasExcludeProperty: parsedCommandLine.raw.exclude !== undefined
};
}
project.configFileSpecs = parsedCommandLine.configFileSpecs;
project.canConfigFileJsonReportNoInputFiles = canJsonReportNoInputFiles(parsedCommandLine.raw);
project.setProjectErrors(configFileErrors);
project.updateReferences(parsedCommandLine.projectReferences);
@@ -2237,11 +2235,11 @@ namespace ts.server {
*/
/*@internal*/
reloadFileNamesOfConfiguredProject(project: ConfiguredProject) {
const configFileSpecs = project.configFileSpecs!; // TODO: GH#18217
const configFileSpecs = project.getCompilerOptions().configFile!.configFileSpecs!;
const configFileName = project.getConfigFilePath();
const fileNamesResult = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), project.getCompilationSettings(), project.getCachedDirectoryStructureHost(), this.hostConfiguration.extraFileExtensions);
project.updateErrorOnNoInputFiles(fileNamesResult);
this.updateNonInferredProjectFiles(project, fileNamesResult.fileNames.concat(project.getExternalFiles()), fileNamePropertyReader);
const fileNames = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), project.getCompilationSettings(), project.getCachedDirectoryStructureHost(), this.hostConfiguration.extraFileExtensions);
project.updateErrorOnNoInputFiles(fileNames);
this.updateNonInferredProjectFiles(project, fileNames.concat(project.getExternalFiles()), fileNamePropertyReader);
return project.updateGraph();
}
+5 -6
View File
@@ -1375,6 +1375,8 @@ namespace ts.server {
for (const file of sourceFiles) {
strBuilder += `\t${file.fileName}\n`;
}
strBuilder += "\n\n";
explainFiles(this.program, s => strBuilder += `\t${s}\n`);
}
return strBuilder;
}
@@ -2066,9 +2068,6 @@ namespace ts.server {
/* @internal */
openFileWatchTriggered = new Map<string, true>();
/*@internal*/
configFileSpecs: ConfigFileSpecs | undefined;
/*@internal*/
canConfigFileJsonReportNoInputFiles = false;
@@ -2293,7 +2292,7 @@ namespace ts.server {
}
this.stopWatchingWildCards();
this.configFileSpecs = undefined;
this.projectErrors = undefined;
this.openFileWatchTriggered.clear();
this.compilerHost = undefined;
super.close();
@@ -2376,8 +2375,8 @@ namespace ts.server {
}
/*@internal*/
updateErrorOnNoInputFiles(fileNameResult: ExpandResult) {
updateErrorForNoInputFiles(fileNameResult, this.getConfigFilePath(), this.configFileSpecs!, this.projectErrors!, this.canConfigFileJsonReportNoInputFiles);
updateErrorOnNoInputFiles(fileNames: string[]) {
updateErrorForNoInputFiles(fileNames, this.getConfigFilePath(), this.getCompilerOptions().configFile!.configFileSpecs!, this.projectErrors!, this.canConfigFileJsonReportNoInputFiles);
}
}
+133 -40
View File
@@ -611,12 +611,22 @@ export = C;
"c.ts",
`/// <reference path="D.ts"/>`.indexOf(`D.ts`),
"D.ts".length,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
"D.ts",
"d.ts",
tscWatch.getDiagnosticMessageChain(
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
["D.ts", "d.ts"],
[
tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[
tscWatch.getDiagnosticMessageChain(Diagnostics.Referenced_via_0_from_file_1, ["D.ts", "c.ts"]),
tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
]
)
],
)
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
relatedInformation: undefined,
}]
);
});
@@ -638,12 +648,22 @@ export = C;
"c.ts",
`import {x} from "D"`.indexOf(`"D"`),
`"D"`.length,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
"/a/b/D.ts",
"d.ts",
tscWatch.getDiagnosticMessageChain(
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
["/a/b/D.ts", "d.ts"],
[
tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[
tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"D"`, "c.ts"]),
tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
]
)
],
)
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
relatedInformation: undefined,
}]
);
});
@@ -665,12 +685,22 @@ export = C;
"moduleA.ts",
`import {x} from "./ModuleB"`.indexOf(`"./ModuleB"`),
`"./ModuleB"`.length,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
"ModuleB.ts",
"moduleB.ts",
tscWatch.getDiagnosticMessageChain(
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
["ModuleB.ts", "moduleB.ts"],
[
tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[
tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./ModuleB"`, "moduleA.ts"]),
tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
]
)
],
)
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
relatedInformation: undefined
}]
);
});
@@ -693,12 +723,22 @@ export = C;
"c.ts",
`import {x} from "D"`.indexOf(`"D"`),
`"D"`.length,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
"/a/b/D.ts",
"d.ts",
tscWatch.getDiagnosticMessageChain(
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
["/a/b/D.ts", "d.ts"],
[
tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[
tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"D"`, "c.ts"]),
tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
]
)
],
)
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
relatedInformation: undefined
}]
);
});
@@ -715,34 +755,65 @@ export = C;
"",
/*useCaseSensitiveFileNames*/ false,
["moduleA.ts", "moduleB.ts", "moduleC.ts"],
program => [
{
program => {
const importInA = {
...tscWatch.getDiagnosticOfFileFromProgram(
program,
"moduleA.ts",
`import a = require("./ModuleC")`.indexOf(`"./ModuleC"`),
`"./ModuleC"`.length,
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
"ModuleC.ts",
"moduleC.ts",
Diagnostics.File_is_included_via_import_here,
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
},
{
};
const importInB = {
...tscWatch.getDiagnosticOfFileFromProgram(
program,
"moduleB.ts",
`import a = require("./moduleC")`.indexOf(`"./moduleC"`),
`"./moduleC"`.length,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
"moduleC.ts",
"ModuleC.ts"
Diagnostics.File_is_included_via_import_here,
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
}
]
};
const importHereInA = tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./ModuleC"`, "moduleA.ts"]);
const importHereInB = tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./moduleC"`, "moduleB.ts"]);
const details = [tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[importHereInA, importHereInB, tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)]
)];
return [
{
...tscWatch.getDiagnosticOfFileFrom(
importInA.file,
importInA.start,
importInA.length,
tscWatch.getDiagnosticMessageChain(
Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
["ModuleC.ts", "moduleC.ts" ],
details,
)
),
relatedInformation: [importInB]
},
{
...tscWatch.getDiagnosticOfFileFrom(
importInB.file,
importInB.start,
importInB.length,
tscWatch.getDiagnosticMessageChain(
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
["moduleC.ts", "ModuleC.ts"],
details,
)
),
relatedInformation: [importInA]
}
];
}
);
});
@@ -768,12 +839,34 @@ import b = require("./moduleB");
"moduleB.ts",
`import a = require("./moduleC")`.indexOf(`"./moduleC"`),
`"./moduleC"`.length,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
"/a/B/c/moduleC.ts",
"/a/B/c/ModuleC.ts"
tscWatch.getDiagnosticMessageChain(
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
["/a/B/c/moduleC.ts", "/a/B/c/ModuleC.ts"],
[
tscWatch.getDiagnosticMessageChain(
Diagnostics.The_file_is_in_the_program_because_Colon,
emptyArray,
[
tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./ModuleC"`, "/a/B/c/moduleA.ts"]),
tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./moduleC"`, "/a/B/c/moduleB.ts"])
]
)
],
)
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
relatedInformation: [
{
...tscWatch.getDiagnosticOfFileFromProgram(
program,
"moduleA.ts",
`import a = require("./ModuleC")`.indexOf(`"./ModuleC"`),
`"./ModuleC"`.length,
Diagnostics.File_is_included_via_import_here,
),
reportsUnnecessary: undefined,
reportsDeprecated: undefined
}
]
}]
);
});
@@ -1384,14 +1477,14 @@ import b = require("./moduleB");
},
};
const program1 = createProgram(names, {}, compilerHost);
const diagnostics1 = program1.getFileProcessingDiagnostics().getDiagnostics();
const diagnostics1 = program1.getOptionsDiagnostics();
assert.equal(diagnostics1.length, 1, "expected one diagnostic");
const program2 = createProgram(names, {}, compilerHost, program1);
assert.isTrue(program2.structureIsReused === StructureIsReused.Completely);
const diagnostics2 = program1.getFileProcessingDiagnostics().getDiagnostics();
const diagnostics2 = program2.getOptionsDiagnostics();
assert.equal(diagnostics2.length, 1, "expected one diagnostic");
assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
assert.deepEqual(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
});
it("Modules in the same .d.ts file are preferred to external files", () => {
+1 -1
View File
@@ -123,7 +123,7 @@ namespace ts {
const program = createProgram(["test.ts"], { module: ModuleKind.ES2015 }, host);
assert(program.getSourceFiles().length === 1, "expected 'getSourceFiles' length to be 1");
assert(program.getMissingFilePaths().length === 0, "expected 'getMissingFilePaths' length to be 0");
assert(program.getFileProcessingDiagnostics().getDiagnostics().length === 0, "expected 'getFileProcessingDiagnostics' length to be 0");
assert((program.getFileProcessingDiagnostics()?.length || 0) === 0, "expected 'getFileProcessingDiagnostics' length to be 0");
});
});
+10 -2
View File
@@ -84,6 +84,7 @@ namespace ts {
ignoreDtsChanged?: true;
ignoreDtsUnchanged?: true;
baselineOnly?: true;
additionalCommandLineArgs?: string[];
}
function verifyOutFileScenario({
@@ -92,7 +93,8 @@ namespace ts {
modifyAgainFs,
ignoreDtsChanged,
ignoreDtsUnchanged,
baselineOnly
baselineOnly,
additionalCommandLineArgs,
}: VerifyOutFileScenarioInput) {
const incrementalScenarios: TscIncremental[] = [];
if (!ignoreDtsChanged) {
@@ -117,7 +119,7 @@ namespace ts {
subScenario,
fs: () => outFileFs,
scenario: "outfile-concat",
commandLineArgs: ["--b", "/src/third", "--verbose"],
commandLineArgs: ["--b", "/src/third", "--verbose", ...(additionalCommandLineArgs || [])],
baselineSourceMap: true,
modifyFs,
baselineReadFileCalls: !baselineOnly,
@@ -133,6 +135,12 @@ namespace ts {
subScenario: "baseline sectioned sourcemaps",
});
verifyOutFileScenario({
subScenario: "explainFiles",
additionalCommandLineArgs: ["--explainFiles"],
baselineOnly: true
});
// Verify baseline with build info + dts unChanged
verifyOutFileScenario({
subScenario: "when final project is not composite but uses project references",
@@ -13,21 +13,21 @@ namespace ts {
scenario: "resolveJsonModule",
subScenario: "include only",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tsconfig_withInclude.json"],
commandLineArgs: ["--b", "/src/tsconfig_withInclude.json", "--v", "--explainFiles"],
});
verifyTsc({
scenario: "resolveJsonModule",
subScenario: "include of json along with other include",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tsconfig_withIncludeOfJson.json"],
commandLineArgs: ["--b", "/src/tsconfig_withIncludeOfJson.json", "--v", "--explainFiles"],
});
verifyTsc({
scenario: "resolveJsonModule",
subScenario: "include of json along with other include and file name matches ts file",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tsconfig_withIncludeOfJson.json"],
commandLineArgs: ["--b", "/src/tsconfig_withIncludeOfJson.json", "--v", "--explainFiles"],
modifyFs: fs => {
fs.rimrafSync("/src/src/hello.json");
fs.writeFileSync("/src/src/index.json", JSON.stringify({ hello: "world" }));
@@ -41,21 +41,21 @@ export default hello.hello`);
scenario: "resolveJsonModule",
subScenario: "files containing json file",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tsconfig_withFiles.json"],
commandLineArgs: ["--b", "/src/tsconfig_withFiles.json", "--v", "--explainFiles"],
});
verifyTsc({
scenario: "resolveJsonModule",
subScenario: "include and files",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tsconfig_withIncludeAndFiles.json"],
commandLineArgs: ["--b", "/src/tsconfig_withIncludeAndFiles.json", "--v", "--explainFiles"],
});
verifyTscSerializedIncrementalEdits({
scenario: "resolveJsonModule",
subScenario: "sourcemap",
fs: () => projFs,
commandLineArgs: ["--b", "src/tsconfig_withFiles.json", "--verbose"],
commandLineArgs: ["--b", "src/tsconfig_withFiles.json", "--verbose", "--explainFiles"],
modifyFs: fs => replaceText(fs, "src/tsconfig_withFiles.json", `"composite": true,`, `"composite": true, "sourceMap": true,`),
incrementalScenarios: noChangeOnlyRuns
});
@@ -75,7 +75,7 @@ export default hello.hello`);
scenario: "resolveJsonModule",
subScenario: "importing json module from project reference",
fs: () => loadProjectFromDisk("tests/projects/importJsonFromProjectReference"),
commandLineArgs: ["--b", "src/tsconfig.json", "--verbose"],
commandLineArgs: ["--b", "src/tsconfig.json", "--verbose", "--explainFiles"],
incrementalScenarios: noChangeOnlyRuns
});
});
@@ -386,6 +386,13 @@ class someClass2 { }`),
commandLineArgs: ["--b", "/src/tests", "--listEmittedFiles"],
incrementalScenarios: coreChanges
});
verifyTscSerializedIncrementalEdits({
scenario: "sample1",
subScenario: "explainFiles",
fs: () => projFs,
commandLineArgs: ["--b", "/src/tests", "--explainFiles", "--v"],
incrementalScenarios: coreChanges
});
});
describe("emit output", () => {
@@ -19,7 +19,7 @@ namespace ts {
scenario: "declarationEmit",
subScenario,
sys: () => tscWatch.createWatchedSystem(files, { currentDirectory: tscWatch.projectRoot }),
commandLineArgs: ["-p", rootProject, "--listFiles"],
commandLineArgs: ["-p", rootProject, "--explainFiles"],
changes: emptyArray
});
});
@@ -33,7 +33,7 @@ namespace ts {
files.map(f => changeCaseFile(f, changeCaseFileTestPath, str => str.replace("myproject", "myProject"))),
{ currentDirectory: tscWatch.projectRoot }
),
commandLineArgs: ["-p", rootProject, "--listFiles"],
commandLineArgs: ["-p", rootProject, "--explainFiles"],
changes: emptyArray
});
});
-3
View File
@@ -37,9 +37,6 @@ namespace ts {
getPrograms: () => readonly CommandLineProgram[];
}
function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T {
return !!(program as T).getState;
}
function isAnyProgram(program: Program | EmitAndSemanticDiagnosticsBuilderProgram | ParsedCommandLine): program is Program | EmitAndSemanticDiagnosticsBuilderProgram {
return !!(program as Program | EmitAndSemanticDiagnosticsBuilderProgram).getCompilerOptions;
}
+1 -1
View File
@@ -16,7 +16,7 @@ namespace ts {
]
}`,
}),
commandLineArgs: ["--incremental", "--p", "src/project", "--tsBuildInfoFile", "src/project/.tsbuildinfo"],
commandLineArgs: ["--incremental", "--p", "src/project", "--tsBuildInfoFile", "src/project/.tsbuildinfo", "--explainFiles"],
incrementalScenarios: noChangeOnlyRuns
});
@@ -46,5 +46,78 @@ namespace ts.tscWatch {
}
]
});
verifyTscWatch({
scenario: "forceConsistentCasingInFileNames",
subScenario: "when relative information file location changes",
commandLineArgs: ["--w", "--p", ".", "--explainFiles"],
sys: () => {
const moduleA: File = {
path: `${projectRoot}/moduleA.ts`,
content: `import a = require("./ModuleC")`
};
const moduleB: File = {
path: `${projectRoot}/moduleB.ts`,
content: `import a = require("./moduleC")`
};
const moduleC: File = {
path: `${projectRoot}/moduleC.ts`,
content: `export const x = 10;`
};
const tsconfig: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ compilerOptions: { forceConsistentCasingInFileNames: true } })
};
return createWatchedSystem([moduleA, moduleB, moduleC, libFile, tsconfig], { currentDirectory: projectRoot });
},
changes: [
{
caption: "Prepend a line to moduleA",
change: sys => sys.prependFile(`${projectRoot}/moduleA.ts`, `// some comment
`),
timeouts: runQueuedTimeoutCallbacks,
}
],
});
verifyTscWatch({
scenario: "forceConsistentCasingInFileNames",
subScenario: "jsxImportSource option changed",
commandLineArgs: ["--w", "--p", ".", "--explainFiles"],
sys: () => createWatchedSystem([
libFile,
{
path: `${projectRoot}/node_modules/react/Jsx-runtime/index.d.ts`,
content: `export namespace JSX {
interface Element {}
interface IntrinsicElements {
div: {
propA?: boolean;
};
}
}
export function jsx(...args: any[]): void;
export function jsxs(...args: any[]): void;
export const Fragment: unique symbol;
`,
},
{
path: `${projectRoot}/node_modules/react/package.json`,
content: JSON.stringify({ name: "react", version: "0.0.1" })
},
{
path: `${projectRoot}/index.tsx`,
content: `export const App = () => <div propA={true}></div>;`
},
{
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({
compilerOptions: { jsx: "react-jsx", jsxImportSource: "react", forceConsistentCasingInFileNames: true },
files: ["node_modules/react/Jsx-runtime/index.d.ts", "index.tsx"]
})
}
], { currentDirectory: projectRoot }),
changes: emptyArray,
});
});
}
+17 -12
View File
@@ -216,27 +216,32 @@ namespace ts.tscWatch {
);
}
export function getDiagnosticMessageChain(message: DiagnosticMessage, args?: (string | number)[], next?: DiagnosticMessageChain[]): DiagnosticMessageChain {
let text = getLocaleSpecificMessage(message);
if (args?.length) {
text = formatStringFromArgs(text, args);
}
return {
messageText: text,
category: message.category,
code: message.code,
next
};
}
function isDiagnosticMessageChain(message: DiagnosticMessage | DiagnosticMessageChain): message is DiagnosticMessageChain {
return !!(message as DiagnosticMessageChain).messageText;
}
export function getDiagnosticOfFileFrom(file: SourceFile | undefined, start: number | undefined, length: number | undefined, message: DiagnosticMessage | DiagnosticMessageChain, ..._args: (string | number)[]): Diagnostic {
let text: DiagnosticMessageChain | string;
if (isDiagnosticMessageChain(message)) {
text = message;
}
else {
text = getLocaleSpecificMessage(message);
if (arguments.length > 4) {
text = formatStringFromArgs(text, arguments, 4);
}
}
export function getDiagnosticOfFileFrom(file: SourceFile | undefined, start: number | undefined, length: number | undefined, message: DiagnosticMessage | DiagnosticMessageChain, ...args: (string | number)[]): Diagnostic {
return {
file,
start,
length,
messageText: text,
messageText: isDiagnosticMessageChain(message) ?
message :
getDiagnosticMessageChain(message, args).messageText,
category: message.category,
code: message.code,
};
@@ -277,8 +277,9 @@ export interface A {
modifyFs: host => host.deleteFile(`${project}/globals.d.ts`)
});
const jsxImportSourceOptions = { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" };
const jsxLibraryContent = `export namespace JSX {
describe("with option jsxImportSource", () => {
const jsxImportSourceOptions = { module: "commonjs", jsx: "react-jsx", incremental: true, jsxImportSource: "react" };
const jsxLibraryContent = `export namespace JSX {
interface Element {}
interface IntrinsicElements {
div: {
@@ -291,64 +292,66 @@ export function jsxs(...args: any[]): void;
export const Fragment: unique symbol;
`;
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource option changed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
{ path: `${project}/node_modules/react/package.json`, content: JSON.stringify({ name: "react", version: "0.0.1" }) },
{ path: `${project}/node_modules/preact/jsx-runtime/index.d.ts`, content: jsxLibraryContent.replace("propA", "propB") },
{ path: `${project}/node_modules/preact/package.json`, content: JSON.stringify({ name: "preact", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => host.writeFile(configFile.path, JSON.stringify({ compilerOptions: { ...jsxImportSourceOptions, jsxImportSource: "preact" } }))
});
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource option changed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
{ path: `${project}/node_modules/react/package.json`, content: JSON.stringify({ name: "react", version: "0.0.1" }) },
{ path: `${project}/node_modules/preact/jsx-runtime/index.d.ts`, content: jsxLibraryContent.replace("propA", "propB") },
{ path: `${project}/node_modules/preact/package.json`, content: JSON.stringify({ name: "preact", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => host.writeFile(configFile.path, JSON.stringify({ compilerOptions: { ...jsxImportSourceOptions, jsxImportSource: "preact" } })),
optionsToExtend: ["--explainFiles"]
});
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource backing types added",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => {
host.createDirectory(`${project}/node_modules`);
host.createDirectory(`${project}/node_modules/react`);
host.createDirectory(`${project}/node_modules/react/jsx-runtime`);
host.writeFile(`${project}/node_modules/react/jsx-runtime/index.d.ts`, jsxLibraryContent);
host.writeFile(`${project}/node_modules/react/package.json`, JSON.stringify({ name: "react", version: "0.0.1" }));
}
});
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource backing types added",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => {
host.createDirectory(`${project}/node_modules`);
host.createDirectory(`${project}/node_modules/react`);
host.createDirectory(`${project}/node_modules/react/jsx-runtime`);
host.writeFile(`${project}/node_modules/react/jsx-runtime/index.d.ts`, jsxLibraryContent);
host.writeFile(`${project}/node_modules/react/package.json`, JSON.stringify({ name: "react", version: "0.0.1" }));
}
});
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource backing types removed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
{ path: `${project}/node_modules/react/package.json`, content: JSON.stringify({ name: "react", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => {
host.deleteFile(`${project}/node_modules/react/jsx-runtime/index.d.ts`);
host.deleteFile(`${project}/node_modules/react/package.json`);
}
});
verifyIncrementalWatchEmit({
subScenario: "jsxImportSource backing types removed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/react/jsx-runtime/index.d.ts`, content: jsxLibraryContent },
{ path: `${project}/node_modules/react/package.json`, content: JSON.stringify({ name: "react", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const App = () => <div propA={true}></div>;` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: jsxImportSourceOptions }) }
],
modifyFs: host => {
host.deleteFile(`${project}/node_modules/react/jsx-runtime/index.d.ts`);
host.deleteFile(`${project}/node_modules/react/package.json`);
}
});
verifyIncrementalWatchEmit({
subScenario: "importHelpers backing types removed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/tslib/index.d.ts`, content: "export function __assign(...args: any[]): any;" },
{ path: `${project}/node_modules/tslib/package.json`, content: JSON.stringify({ name: "tslib", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const x = {...{}};` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: { importHelpers: true } }) }
],
modifyFs: host => {
host.deleteFile(`${project}/node_modules/tslib/index.d.ts`);
host.deleteFile(`${project}/node_modules/tslib/package.json`);
}
verifyIncrementalWatchEmit({
subScenario: "importHelpers backing types removed",
files: () => [
{ path: libFile.path, content: libContent },
{ path: `${project}/node_modules/tslib/index.d.ts`, content: "export function __assign(...args: any[]): any;" },
{ path: `${project}/node_modules/tslib/package.json`, content: JSON.stringify({ name: "tslib", version: "0.0.1" }) },
{ path: `${project}/index.tsx`, content: `export const x = {...{}};` },
{ path: configFile.path, content: JSON.stringify({ compilerOptions: { importHelpers: true } }) }
],
modifyFs: host => {
host.deleteFile(`${project}/node_modules/tslib/index.d.ts`);
host.deleteFile(`${project}/node_modules/tslib/package.json`);
}
});
});
});
}
@@ -117,7 +117,7 @@ namespace ts.tscWatch {
verifyTscWatch({
scenario,
subScenario: "handle recreated files correctly",
commandLineArgs: ["-w", "-p", configFilePath],
commandLineArgs: ["-w", "-p", configFilePath, "--explainFiles"],
sys: () => {
return createWatchedSystem([libFile, commonFile1, commonFile2, configFile]);
},
@@ -159,7 +159,7 @@ namespace ts.tscWatch {
verifyTscWatch({
scenario,
subScenario: "should reflect change in config file",
commandLineArgs: ["-w", "-p", configFilePath],
commandLineArgs: ["-w", "-p", configFilePath, "--explainFiles"],
sys: () => {
const configFile: File = {
path: configFilePath,
@@ -373,7 +373,7 @@ export class A {
verifyTscWatch({
scenario,
subScenario: "changes in files are reflected in project structure",
commandLineArgs: ["-w", "/a/b/f1.ts"],
commandLineArgs: ["-w", "/a/b/f1.ts", "--explainFiles"],
sys: () => {
const file1 = {
path: "/a/b/f1.ts",
@@ -72,7 +72,7 @@ namespace ts.projectSystem {
content: "{}"
};
const host = createServerHost([f1, libFile, configFile, configFile2]);
const service = createProjectService(host, { useSingleInferredProject: true }, { useInferredProjectPerProjectRoot: true });
const service = createProjectService(host, { useSingleInferredProject: true, useInferredProjectPerProjectRoot: true });
service.openClientFile(f1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, projectDir);
checkNumberOfProjects(service, { configuredProjects: 1 });
assert.isDefined(service.configuredProjects.get(configFile.path));
@@ -108,7 +108,7 @@ var x = 10;`
content: "const y = 10"
};
const host = createServerHost([config, file, libFile], { useCaseSensitiveFileNames: true });
const service = createProjectService(host, /*parameters*/ undefined, { useInferredProjectPerProjectRoot: true });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true });
service.openClientFile(untitledFile, "const x = 10;", /*scriptKind*/ undefined, tscWatch.projectRoot);
checkNumberOfProjects(service, { inferredProjects: 1 });
checkProjectActualFiles(service.inferredProjects[0], [untitledFile, libFile.path]);
@@ -146,8 +146,17 @@ namespace ts.projectSystem {
semantic: [createDiagnostic(
location.start,
location.end,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
[loggerFile.path.toLowerCase(), loggerFile.path]
{
message: Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
args: [loggerFile.path.toLowerCase(), loggerFile.path],
next: [{
message: Diagnostics.The_file_is_in_the_program_because_Colon,
next: [
{ message: Diagnostics.Matched_by_include_pattern_0_in_1, args: ["**/*", tsconfig.path] },
{ message: Diagnostics.Imported_via_0_from_file_1, args: [`"./logger"`, anotherFile.path] }
]
}]
}
)],
suggestion: []
}]
+40 -17
View File
@@ -383,17 +383,9 @@ namespace ts.projectSystem {
}
}
export interface CreateProjectServiceParameters {
cancellationToken?: HostCancellationToken;
logger?: server.Logger;
useSingleInferredProject?: boolean;
typingsInstaller?: server.ITypingsInstaller;
eventHandler?: server.ProjectServiceEventHandler;
}
export class TestProjectService extends server.ProjectService {
constructor(host: server.ServerHost, logger: server.Logger, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean,
typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler, opts: Partial<server.ProjectServiceOptions> = {}) {
typingsInstaller: server.ITypingsInstaller, opts: Partial<server.ProjectServiceOptions> = {}) {
super({
host,
logger,
@@ -402,7 +394,6 @@ namespace ts.projectSystem {
useInferredProjectPerProjectRoot: false,
typingsInstaller,
typesMapLocation: customTypesMap.path,
eventHandler,
...opts
});
}
@@ -411,11 +402,12 @@ namespace ts.projectSystem {
checkNumberOfProjects(this, count);
}
}
export function createProjectService(host: server.ServerHost, parameters: CreateProjectServiceParameters = {}, options?: Partial<server.ProjectServiceOptions>) {
const cancellationToken = parameters.cancellationToken || server.nullCancellationToken;
const logger = parameters.logger || createHasErrorMessageLogger().logger;
const useSingleInferredProject = parameters.useSingleInferredProject !== undefined ? parameters.useSingleInferredProject : false;
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller!, parameters.eventHandler!, options); // TODO: GH#18217
export function createProjectService(host: server.ServerHost, options?: Partial<server.ProjectServiceOptions>) {
const cancellationToken = options?.cancellationToken || server.nullCancellationToken;
const logger = options?.logger || createHasErrorMessageLogger().logger;
const useSingleInferredProject = options?.useSingleInferredProject !== undefined ? options.useSingleInferredProject : false;
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, options?.typingsInstaller || server.nullTypingsInstaller, options);
}
export function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
@@ -700,8 +692,39 @@ namespace ts.projectSystem {
checkNthEvent(session, server.toEvent(eventName, diagnostics), 0, isMostRecent);
}
export function createDiagnostic(start: protocol.Location, end: protocol.Location, message: DiagnosticMessage, args: readonly string[] = [], category = diagnosticCategoryName(message), reportsUnnecessary?: {}, relatedInformation?: protocol.DiagnosticRelatedInformation[], reportsDeprecated?: {}): protocol.Diagnostic {
return { start, end, text: formatStringFromArgs(message.message, args), code: message.code, category, reportsUnnecessary, reportsDeprecated, relatedInformation, source: undefined };
interface DiagnosticChainText {
message: DiagnosticMessage;
args?: readonly string[];
next?: DiagnosticChainText[];
}
function isDiagnosticMessage(diagnostic: DiagnosticMessage | DiagnosticChainText): diagnostic is DiagnosticMessage {
return !!diagnostic && isString((diagnostic as DiagnosticMessage).message);
}
function getTextForDiagnostic(diag: DiagnosticMessage | DiagnosticChainText | undefined, args?: readonly string[], indent = 0) {
if (diag === undefined) return "";
if (isDiagnosticMessage(diag)) return formatStringFromArgs(diag.message, args || emptyArray);
let result = "";
if (indent) {
result += "\n";
for (let i = 0; i < indent; i++) {
result += " ";
}
}
result += formatStringFromArgs(diag.message.message, diag.args || emptyArray);
indent++;
if (diag.next) {
for (const kid of diag.next) {
result += getTextForDiagnostic(kid, /*args*/ undefined, indent);
}
}
return result;
}
export function createDiagnostic(start: protocol.Location, end: protocol.Location, message: DiagnosticMessage | DiagnosticChainText, args?: readonly string[], category?: string, reportsUnnecessary?: {}, relatedInformation?: protocol.DiagnosticRelatedInformation[], reportsDeprecated?: {}): protocol.Diagnostic {
const diag = isDiagnosticMessage(message) ? message : message.message;
return { start, end, text: getTextForDiagnostic(message, args), code: diag.code, category: category || diagnosticCategoryName(diag), reportsUnnecessary, reportsDeprecated, relatedInformation, source: undefined };
}
export function checkCompleteEvent(session: TestSession, numberOfCurrentEvents: number, expectedSequenceId: number, isMostRecent = true): void {
@@ -78,7 +78,7 @@ namespace ts.projectSystem {
};
const host = createServerHost([file1]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { syntaxOnly: true });
const projectService = createProjectService(host, { useSingleInferredProject: true, syntaxOnly: true });
projectService.openClientFile(file1.path, file1.content);
@@ -250,7 +250,7 @@ namespace ts.projectSystem {
{ path: "/c/file3.ts", content: "let z = 4;" }
];
const host = createServerHost(files, { useCaseSensitiveFileNames });
const projectService = createProjectService(host, { useSingleInferredProject: true, }, { useInferredProjectPerProjectRoot: true });
const projectService = createProjectService(host, { useSingleInferredProject: true, useInferredProjectPerProjectRoot: true });
projectService.setCompilerOptionsForInferredProjects({
allowJs: true,
target: ScriptTarget.ESNext
@@ -118,7 +118,7 @@ namespace ts.projectSystem {
const projName = "proj1";
const host = createServerHost([file1, file2]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { eventHandler: noop });
const projectService = createProjectService(host, { useSingleInferredProject: true, eventHandler: noop });
projectService.openExternalProject({ rootFiles: toExternalFiles([file1.path, file2.path]), options: {}, projectFileName: projName });
const proj1 = projectService.findProject(projName)!;
@@ -145,7 +145,7 @@ namespace ts.projectSystem {
const externalProjectName = "externalproject";
const host = createServerHost([file1, config1]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { syntaxOnly: true });
const projectService = createProjectService(host, { useSingleInferredProject: true, syntaxOnly: true });
projectService.openExternalProject({
rootFiles: toExternalFiles([file1.path, config1.path]),
options: {},
@@ -175,7 +175,7 @@ namespace ts.projectSystem {
};
const host = createServerHost([file1, config1]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { syntaxOnly: true });
const projectService = createProjectService(host, { useSingleInferredProject: true, syntaxOnly: true });
projectService.openClientFile(file1.path, file1.content);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
@@ -201,7 +201,7 @@ namespace ts.projectSystem {
};
const host = createServerHost([file1, config1]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { syntaxOnly: true });
const projectService = createProjectService(host, { useSingleInferredProject: true, syntaxOnly: true });
projectService.applyChangesInOpenFiles(singleIterator({ fileName: file1.path, content: file1.content }));
checkNumberOfProjects(projectService, { inferredProjects: 1 });
@@ -62,7 +62,7 @@ namespace ts.projectSystem {
it("inferred project", () => {
const host = createServerHost([libFile, file1, file2]);
const service = createProjectService(host, /*parameters*/ undefined, { useInferredProjectPerProjectRoot: true, });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true, });
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
const timeoutId = host.getNextTimeoutId();
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["node_modules"] }, tscWatch.projectRoot);
@@ -700,7 +700,7 @@ namespace ts.projectSystem {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host, {}, { useInferredProjectPerProjectRoot: true });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true });
setupConfigureHost(service, configureHost);
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["node_modules"] }, tscWatch.projectRoot);
service.openClientFile(main.path, main.content, ScriptKind.TS, tscWatch.projectRoot);
@@ -743,7 +743,7 @@ namespace ts.projectSystem {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host, {}, { useInferredProjectPerProjectRoot: true });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true });
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["**/../*"] }, tscWatch.projectRoot);
service.openClientFile(main.path, main.content, ScriptKind.TS, tscWatch.projectRoot);
const project = service.inferredProjects[0];