mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-24 04:30:53 -06:00
Merge pull request #31101 from Microsoft/cacheParseConfigFile
Caching results of parsing Config file and extended file
This commit is contained in:
commit
9efea31649
@ -1346,7 +1346,12 @@ namespace ts {
|
||||
/**
|
||||
* Reads the config file, reports errors if any and exits if the config file cannot be found
|
||||
*/
|
||||
export function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined {
|
||||
export function getParsedCommandLineOfConfigFile(
|
||||
configFileName: string,
|
||||
optionsToExtend: CompilerOptions,
|
||||
host: ParseConfigFileHost,
|
||||
extendedConfigCache?: Map<ExtendedConfigCacheEntry>
|
||||
): ParsedCommandLine | undefined {
|
||||
let configFileText: string | undefined;
|
||||
try {
|
||||
configFileText = host.readFile(configFileName);
|
||||
@ -1367,7 +1372,16 @@ namespace ts {
|
||||
result.path = toPath(configFileName, cwd, createGetCanonicalFileName(host.useCaseSensitiveFileNames));
|
||||
result.resolvedPath = result.path;
|
||||
result.originalFileName = result.fileName;
|
||||
return parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
|
||||
return parseJsonSourceFileConfigFileContent(
|
||||
result,
|
||||
host,
|
||||
getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd),
|
||||
optionsToExtend,
|
||||
getNormalizedAbsolutePath(configFileName, cwd),
|
||||
/*resolutionStack*/ undefined,
|
||||
/*extraFileExtension*/ undefined,
|
||||
extendedConfigCache
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1981,8 +1995,8 @@ namespace ts {
|
||||
* @param basePath A root directory to resolve relative path entries in the config
|
||||
* file to. e.g. outDir
|
||||
*/
|
||||
export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray<FileExtensionInfo>): ParsedCommandLine {
|
||||
return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions);
|
||||
export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray<FileExtensionInfo>, /*@internal*/ extendedConfigCache?: Map<ExtendedConfigCacheEntry>): ParsedCommandLine {
|
||||
return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
@ -2021,11 +2035,12 @@ namespace ts {
|
||||
configFileName?: string,
|
||||
resolutionStack: Path[] = [],
|
||||
extraFileExtensions: ReadonlyArray<FileExtensionInfo> = [],
|
||||
extendedConfigCache?: Map<ExtendedConfigCacheEntry>
|
||||
): ParsedCommandLine {
|
||||
Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined));
|
||||
const errors: Diagnostic[] = [];
|
||||
|
||||
const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors);
|
||||
const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors, extendedConfigCache);
|
||||
const { raw } = parsedConfig;
|
||||
const options = extend(existingOptions, parsedConfig.options || {});
|
||||
options.configFilePath = configFileName && normalizeSlashes(configFileName);
|
||||
@ -2173,7 +2188,7 @@ namespace ts {
|
||||
return existingErrors !== configParseDiagnostics.length;
|
||||
}
|
||||
|
||||
interface ParsedTsconfig {
|
||||
export interface ParsedTsconfig {
|
||||
raw: any;
|
||||
options?: CompilerOptions;
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
@ -2192,13 +2207,14 @@ namespace ts {
|
||||
* It does *not* resolve the included files.
|
||||
*/
|
||||
function parseConfig(
|
||||
json: any,
|
||||
sourceFile: TsConfigSourceFile | undefined,
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
configFileName: string | undefined,
|
||||
resolutionStack: string[],
|
||||
errors: Push<Diagnostic>,
|
||||
json: any,
|
||||
sourceFile: TsConfigSourceFile | undefined,
|
||||
host: ParseConfigHost,
|
||||
basePath: string,
|
||||
configFileName: string | undefined,
|
||||
resolutionStack: string[],
|
||||
errors: Push<Diagnostic>,
|
||||
extendedConfigCache?: Map<ExtendedConfigCacheEntry>
|
||||
): ParsedTsconfig {
|
||||
basePath = normalizeSlashes(basePath);
|
||||
const resolvedPath = getNormalizedAbsolutePath(configFileName || "", basePath);
|
||||
@ -2215,7 +2231,7 @@ namespace ts {
|
||||
if (ownConfig.extendedConfigPath) {
|
||||
// copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios.
|
||||
resolutionStack = resolutionStack.concat([resolvedPath]);
|
||||
const extendedConfig = getExtendedConfig(sourceFile, ownConfig.extendedConfigPath, host, basePath, resolutionStack, errors);
|
||||
const extendedConfig = getExtendedConfig(sourceFile, ownConfig.extendedConfigPath, host, basePath, resolutionStack, errors, extendedConfigCache);
|
||||
if (extendedConfig && isSuccessfulParsedTsconfig(extendedConfig)) {
|
||||
const baseRaw = extendedConfig.raw;
|
||||
const raw = ownConfig.raw;
|
||||
@ -2359,6 +2375,11 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export interface ExtendedConfigCacheEntry {
|
||||
extendedResult: TsConfigSourceFile;
|
||||
extendedConfig: ParsedTsconfig | undefined;
|
||||
}
|
||||
|
||||
function getExtendedConfig(
|
||||
sourceFile: TsConfigSourceFile | undefined,
|
||||
extendedConfigPath: string,
|
||||
@ -2366,40 +2387,53 @@ namespace ts {
|
||||
basePath: string,
|
||||
resolutionStack: string[],
|
||||
errors: Push<Diagnostic>,
|
||||
extendedConfigCache?: Map<ExtendedConfigCacheEntry>
|
||||
): ParsedTsconfig | undefined {
|
||||
const extendedResult = readJsonConfigFile(extendedConfigPath, path => host.readFile(path));
|
||||
const path = host.useCaseSensitiveFileNames ? extendedConfigPath : toLowerCase(extendedConfigPath);
|
||||
let value: ExtendedConfigCacheEntry | undefined;
|
||||
let extendedResult: TsConfigSourceFile;
|
||||
let extendedConfig: ParsedTsconfig | undefined;
|
||||
if (extendedConfigCache && (value = extendedConfigCache.get(path))) {
|
||||
({ extendedResult, extendedConfig } = value);
|
||||
}
|
||||
else {
|
||||
extendedResult = readJsonConfigFile(extendedConfigPath, path => host.readFile(path));
|
||||
if (!extendedResult.parseDiagnostics.length) {
|
||||
const extendedDirname = getDirectoryPath(extendedConfigPath);
|
||||
extendedConfig = parseConfig(/*json*/ undefined, extendedResult, host, extendedDirname,
|
||||
getBaseFileName(extendedConfigPath), resolutionStack, errors, extendedConfigCache);
|
||||
|
||||
if (isSuccessfulParsedTsconfig(extendedConfig)) {
|
||||
// Update the paths to reflect base path
|
||||
const relativeDifference = convertToRelativePath(extendedDirname, basePath, identity);
|
||||
const updatePath = (path: string) => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path);
|
||||
const mapPropertiesInRawIfNotUndefined = (propertyName: string) => {
|
||||
if (raw[propertyName]) {
|
||||
raw[propertyName] = map(raw[propertyName], updatePath);
|
||||
}
|
||||
};
|
||||
|
||||
const { raw } = extendedConfig;
|
||||
mapPropertiesInRawIfNotUndefined("include");
|
||||
mapPropertiesInRawIfNotUndefined("exclude");
|
||||
mapPropertiesInRawIfNotUndefined("files");
|
||||
}
|
||||
}
|
||||
if (extendedConfigCache) {
|
||||
extendedConfigCache.set(path, { extendedResult, extendedConfig });
|
||||
}
|
||||
}
|
||||
if (sourceFile) {
|
||||
sourceFile.extendedSourceFiles = [extendedResult.fileName];
|
||||
if (extendedResult.extendedSourceFiles) {
|
||||
sourceFile.extendedSourceFiles.push(...extendedResult.extendedSourceFiles);
|
||||
}
|
||||
}
|
||||
if (extendedResult.parseDiagnostics.length) {
|
||||
errors.push(...extendedResult.parseDiagnostics);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const extendedDirname = getDirectoryPath(extendedConfigPath);
|
||||
const extendedConfig = parseConfig(/*json*/ undefined, extendedResult, host, extendedDirname,
|
||||
getBaseFileName(extendedConfigPath), resolutionStack, errors);
|
||||
if (sourceFile && extendedResult.extendedSourceFiles) {
|
||||
sourceFile.extendedSourceFiles!.push(...extendedResult.extendedSourceFiles);
|
||||
}
|
||||
|
||||
if (isSuccessfulParsedTsconfig(extendedConfig)) {
|
||||
// Update the paths to reflect base path
|
||||
const relativeDifference = convertToRelativePath(extendedDirname, basePath, identity);
|
||||
const updatePath = (path: string) => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path);
|
||||
const mapPropertiesInRawIfNotUndefined = (propertyName: string) => {
|
||||
if (raw[propertyName]) {
|
||||
raw[propertyName] = map(raw[propertyName], updatePath);
|
||||
}
|
||||
};
|
||||
|
||||
const { raw } = extendedConfig;
|
||||
mapPropertiesInRawIfNotUndefined("include");
|
||||
mapPropertiesInRawIfNotUndefined("exclude");
|
||||
mapPropertiesInRawIfNotUndefined("files");
|
||||
}
|
||||
|
||||
return extendedConfig;
|
||||
return extendedConfig!;
|
||||
}
|
||||
|
||||
function convertCompileOnSaveOptionFromJson(jsonOption: any, basePath: string, errors: Push<Diagnostic>): boolean {
|
||||
|
||||
@ -2678,18 +2678,32 @@ namespace ts {
|
||||
return fromCache || undefined;
|
||||
}
|
||||
|
||||
// An absolute path pointing to the containing directory of the config file
|
||||
const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory());
|
||||
const sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile | undefined;
|
||||
addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
|
||||
if (sourceFile === undefined) {
|
||||
projectReferenceRedirects.set(sourceFilePath, false);
|
||||
return undefined;
|
||||
let commandLine: ParsedCommandLine | undefined;
|
||||
let sourceFile: JsonSourceFile | undefined;
|
||||
if (host.getParsedCommandLine) {
|
||||
commandLine = host.getParsedCommandLine(refPath);
|
||||
if (!commandLine) {
|
||||
addFileToFilesByName(/*sourceFile*/ undefined, sourceFilePath, /*redirectedPath*/ undefined);
|
||||
projectReferenceRedirects.set(sourceFilePath, false);
|
||||
return undefined;
|
||||
}
|
||||
sourceFile = Debug.assertDefined(commandLine.options.configFile);
|
||||
addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
|
||||
}
|
||||
else {
|
||||
// An absolute path pointing to the containing directory of the config file
|
||||
const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory());
|
||||
sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile | undefined;
|
||||
addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
|
||||
if (sourceFile === undefined) {
|
||||
projectReferenceRedirects.set(sourceFilePath, false);
|
||||
return undefined;
|
||||
}
|
||||
sourceFile.path = sourceFilePath;
|
||||
sourceFile.resolvedPath = sourceFilePath;
|
||||
sourceFile.originalFileName = refPath;
|
||||
commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
|
||||
}
|
||||
sourceFile.path = sourceFilePath;
|
||||
sourceFile.resolvedPath = sourceFilePath;
|
||||
sourceFile.originalFileName = refPath;
|
||||
const commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
|
||||
const resolvedRef: ResolvedProjectReference = { commandLine, sourceFile };
|
||||
projectReferenceRedirects.set(sourceFilePath, resolvedRef);
|
||||
if (commandLine.projectReferences) {
|
||||
|
||||
@ -399,12 +399,14 @@ namespace ts {
|
||||
let projectCompilerOptions = baseCompilerOptions;
|
||||
const compilerHost = createCompilerHostFromProgramHost(host, () => projectCompilerOptions);
|
||||
setGetSourceFileAsHashVersioned(compilerHost, host);
|
||||
compilerHost.getParsedCommandLine = parseConfigFile;
|
||||
|
||||
compilerHost.resolveModuleNames = maybeBind(host, host.resolveModuleNames);
|
||||
compilerHost.resolveTypeReferenceDirectives = maybeBind(host, host.resolveTypeReferenceDirectives);
|
||||
let moduleResolutionCache = !compilerHost.resolveModuleNames ? createModuleResolutionCache(currentDirectory, getCanonicalFileName) : undefined;
|
||||
|
||||
const buildInfoChecked = createFileMap<true>(toPath);
|
||||
let extendedConfigCache: Map<ExtendedConfigCacheEntry> | undefined;
|
||||
|
||||
// Watch state
|
||||
const builderPrograms = createFileMap<T>(toPath);
|
||||
@ -481,7 +483,7 @@ namespace ts {
|
||||
|
||||
let diagnostic: Diagnostic | undefined;
|
||||
parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = d => diagnostic = d;
|
||||
const parsed = getParsedCommandLineOfConfigFile(configFilePath, baseCompilerOptions, parseConfigFileHost);
|
||||
const parsed = getParsedCommandLineOfConfigFile(configFilePath, baseCompilerOptions, parseConfigFileHost, extendedConfigCache);
|
||||
parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = noop;
|
||||
configFileCache.setValue(configFilePath, parsed || diagnostic!);
|
||||
return parsed;
|
||||
@ -1395,6 +1397,7 @@ namespace ts {
|
||||
} = changeCompilerHostLikeToUseCache(host, toPath, (...args) => savedGetSourceFile.call(compilerHost, ...args));
|
||||
readFileWithCache = newReadFileWithCache;
|
||||
compilerHost.getSourceFile = getSourceFileWithCache!;
|
||||
extendedConfigCache = createMap();
|
||||
|
||||
const originalResolveModuleNames = compilerHost.resolveModuleNames;
|
||||
if (!compilerHost.resolveModuleNames) {
|
||||
@ -1463,6 +1466,7 @@ namespace ts {
|
||||
host.writeFile = originalWriteFile;
|
||||
compilerHost.getSourceFile = savedGetSourceFile;
|
||||
readFileWithCache = savedReadFileWithCache;
|
||||
extendedConfigCache = undefined;
|
||||
compilerHost.resolveModuleNames = originalResolveModuleNames;
|
||||
moduleResolutionCache = undefined;
|
||||
return anyFailed ? ExitStatus.DiagnosticsPresent_OutputsSkipped : ExitStatus.Success;
|
||||
|
||||
@ -5131,6 +5131,7 @@ namespace ts {
|
||||
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
|
||||
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
|
||||
createHash?(data: string): string;
|
||||
getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined;
|
||||
|
||||
// TODO: later handle this in better way in builder host instead once the api for tsbuild finalizes and doesnt use compilerHost as base
|
||||
/*@internal*/createDirectory?(directory: string): void;
|
||||
|
||||
@ -2761,6 +2761,7 @@ declare namespace ts {
|
||||
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): (ResolvedTypeReferenceDirective | undefined)[];
|
||||
getEnvironmentVariable?(name: string): string | undefined;
|
||||
createHash?(data: string): string;
|
||||
getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined;
|
||||
}
|
||||
interface SourceMapRange extends TextRange {
|
||||
source?: SourceMapSource;
|
||||
@ -3632,7 +3633,7 @@ declare namespace ts {
|
||||
/**
|
||||
* Reads the config file, reports errors if any and exits if the config file cannot be found
|
||||
*/
|
||||
function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined;
|
||||
function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost, extendedConfigCache?: Map<ExtendedConfigCacheEntry>): ParsedCommandLine | undefined;
|
||||
/**
|
||||
* Read tsconfig.json file
|
||||
* @param fileName The path to the config file
|
||||
@ -3674,7 +3675,20 @@ declare namespace ts {
|
||||
* @param basePath A root directory to resolve relative path entries in the config
|
||||
* file to. e.g. outDir
|
||||
*/
|
||||
function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray<FileExtensionInfo>): ParsedCommandLine;
|
||||
function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray<FileExtensionInfo>, /*@internal*/ extendedConfigCache?: Map<ExtendedConfigCacheEntry>): ParsedCommandLine;
|
||||
interface ParsedTsconfig {
|
||||
raw: any;
|
||||
options?: CompilerOptions;
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
/**
|
||||
* Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet
|
||||
*/
|
||||
extendedConfigPath?: string;
|
||||
}
|
||||
interface ExtendedConfigCacheEntry {
|
||||
extendedResult: TsConfigSourceFile;
|
||||
extendedConfig: ParsedTsconfig | undefined;
|
||||
}
|
||||
function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): {
|
||||
options: CompilerOptions;
|
||||
errors: Diagnostic[];
|
||||
|
||||
18
tests/baselines/reference/api/typescript.d.ts
vendored
18
tests/baselines/reference/api/typescript.d.ts
vendored
@ -2761,6 +2761,7 @@ declare namespace ts {
|
||||
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): (ResolvedTypeReferenceDirective | undefined)[];
|
||||
getEnvironmentVariable?(name: string): string | undefined;
|
||||
createHash?(data: string): string;
|
||||
getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined;
|
||||
}
|
||||
interface SourceMapRange extends TextRange {
|
||||
source?: SourceMapSource;
|
||||
@ -3632,7 +3633,7 @@ declare namespace ts {
|
||||
/**
|
||||
* Reads the config file, reports errors if any and exits if the config file cannot be found
|
||||
*/
|
||||
function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined;
|
||||
function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost, extendedConfigCache?: Map<ExtendedConfigCacheEntry>): ParsedCommandLine | undefined;
|
||||
/**
|
||||
* Read tsconfig.json file
|
||||
* @param fileName The path to the config file
|
||||
@ -3674,7 +3675,20 @@ declare namespace ts {
|
||||
* @param basePath A root directory to resolve relative path entries in the config
|
||||
* file to. e.g. outDir
|
||||
*/
|
||||
function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray<FileExtensionInfo>): ParsedCommandLine;
|
||||
function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray<FileExtensionInfo>, /*@internal*/ extendedConfigCache?: Map<ExtendedConfigCacheEntry>): ParsedCommandLine;
|
||||
interface ParsedTsconfig {
|
||||
raw: any;
|
||||
options?: CompilerOptions;
|
||||
typeAcquisition?: TypeAcquisition;
|
||||
/**
|
||||
* Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet
|
||||
*/
|
||||
extendedConfigPath?: string;
|
||||
}
|
||||
interface ExtendedConfigCacheEntry {
|
||||
extendedResult: TsConfigSourceFile;
|
||||
extendedConfig: ParsedTsconfig | undefined;
|
||||
}
|
||||
function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): {
|
||||
options: CompilerOptions;
|
||||
errors: Diagnostic[];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user