mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-13 22:00:59 -05:00
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:
@@ -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:
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user