mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
More functions moved from system to WatchCompilerHost
This commit is contained in:
+6
-213
@@ -1398,6 +1398,9 @@ namespace ts {
|
||||
/** Returns its argument. */
|
||||
export function identity<T>(x: T) { return x; }
|
||||
|
||||
/** Returns lower case string */
|
||||
export function toLowerCase(x: string) { return x.toLowerCase(); }
|
||||
|
||||
/** Throws an error because a function is not implemented. */
|
||||
export function notImplemented(): never {
|
||||
throw new Error("Not implemented");
|
||||
@@ -2877,9 +2880,7 @@ namespace ts {
|
||||
|
||||
export type GetCanonicalFileName = (fileName: string) => string;
|
||||
export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): GetCanonicalFileName {
|
||||
return useCaseSensitiveFileNames
|
||||
? ((fileName) => fileName)
|
||||
: ((fileName) => fileName.toLowerCase());
|
||||
return useCaseSensitiveFileNames ? identity : toLowerCase;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3000,215 +3001,7 @@ namespace ts {
|
||||
|
||||
export function assertTypeIsNever(_: never): void { } // tslint:disable-line no-empty
|
||||
|
||||
export interface FileAndDirectoryExistence {
|
||||
fileExists: boolean;
|
||||
directoryExists: boolean;
|
||||
}
|
||||
|
||||
export interface CachedDirectoryStructureHost extends DirectoryStructureHost {
|
||||
/** Returns the queried result for the file exists and directory exists if at all it was done */
|
||||
addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): FileAndDirectoryExistence | undefined;
|
||||
addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind): void;
|
||||
clearCache(): void;
|
||||
}
|
||||
|
||||
interface MutableFileSystemEntries {
|
||||
readonly files: string[];
|
||||
readonly directories: string[];
|
||||
}
|
||||
|
||||
export function createCachedDirectoryStructureHost(host: DirectoryStructureHost): CachedDirectoryStructureHost {
|
||||
const cachedReadDirectoryResult = createMap<MutableFileSystemEntries>();
|
||||
const getCurrentDirectory = memoize(() => host.getCurrentDirectory());
|
||||
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
|
||||
return {
|
||||
useCaseSensitiveFileNames: host.useCaseSensitiveFileNames,
|
||||
readFile: (path, encoding) => host.readFile(path, encoding),
|
||||
writeFile,
|
||||
fileExists,
|
||||
directoryExists,
|
||||
createDirectory,
|
||||
getCurrentDirectory,
|
||||
getDirectories,
|
||||
readDirectory,
|
||||
addOrDeleteFileOrDirectory,
|
||||
addOrDeleteFile,
|
||||
clearCache,
|
||||
exit: code => host.exit(code)
|
||||
};
|
||||
|
||||
function toPath(fileName: string) {
|
||||
return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName);
|
||||
}
|
||||
|
||||
function getCachedFileSystemEntries(rootDirPath: Path): MutableFileSystemEntries | undefined {
|
||||
return cachedReadDirectoryResult.get(rootDirPath);
|
||||
}
|
||||
|
||||
function getCachedFileSystemEntriesForBaseDir(path: Path): MutableFileSystemEntries | undefined {
|
||||
return getCachedFileSystemEntries(getDirectoryPath(path));
|
||||
}
|
||||
|
||||
function getBaseNameOfFileName(fileName: string) {
|
||||
return getBaseFileName(normalizePath(fileName));
|
||||
}
|
||||
|
||||
function createCachedFileSystemEntries(rootDir: string, rootDirPath: Path) {
|
||||
const resultFromHost: MutableFileSystemEntries = {
|
||||
files: map(host.readDirectory(rootDir, /*extensions*/ undefined, /*exclude*/ undefined, /*include*/["*.*"]), getBaseNameOfFileName) || [],
|
||||
directories: host.getDirectories(rootDir) || []
|
||||
};
|
||||
|
||||
cachedReadDirectoryResult.set(rootDirPath, resultFromHost);
|
||||
return resultFromHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the readDirectory result was already cached, it returns that
|
||||
* Otherwise gets result from host and caches it.
|
||||
* The host request is done under try catch block to avoid caching incorrect result
|
||||
*/
|
||||
function tryReadDirectory(rootDir: string, rootDirPath: Path): MutableFileSystemEntries | undefined {
|
||||
const cachedResult = getCachedFileSystemEntries(rootDirPath);
|
||||
if (cachedResult) {
|
||||
return cachedResult;
|
||||
}
|
||||
|
||||
try {
|
||||
return createCachedFileSystemEntries(rootDir, rootDirPath);
|
||||
}
|
||||
catch (_e) {
|
||||
// If there is exception to read directories, dont cache the result and direct the calls to host
|
||||
Debug.assert(!cachedReadDirectoryResult.has(rootDirPath));
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function fileNameEqual(name1: string, name2: string) {
|
||||
return getCanonicalFileName(name1) === getCanonicalFileName(name2);
|
||||
}
|
||||
|
||||
function hasEntry(entries: ReadonlyArray<string>, name: string) {
|
||||
return some(entries, file => fileNameEqual(file, name));
|
||||
}
|
||||
|
||||
function updateFileSystemEntry(entries: string[], baseName: string, isValid: boolean) {
|
||||
if (hasEntry(entries, baseName)) {
|
||||
if (!isValid) {
|
||||
return filterMutate(entries, entry => !fileNameEqual(entry, baseName));
|
||||
}
|
||||
}
|
||||
else if (isValid) {
|
||||
return entries.push(baseName);
|
||||
}
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void {
|
||||
const path = toPath(fileName);
|
||||
const result = getCachedFileSystemEntriesForBaseDir(path);
|
||||
if (result) {
|
||||
updateFilesOfFileSystemEntry(result, getBaseNameOfFileName(fileName), /*fileExists*/ true);
|
||||
}
|
||||
return host.writeFile(fileName, data, writeByteOrderMark);
|
||||
}
|
||||
|
||||
function fileExists(fileName: string): boolean {
|
||||
const path = toPath(fileName);
|
||||
const result = getCachedFileSystemEntriesForBaseDir(path);
|
||||
return result && hasEntry(result.files, getBaseNameOfFileName(fileName)) ||
|
||||
host.fileExists(fileName);
|
||||
}
|
||||
|
||||
function directoryExists(dirPath: string): boolean {
|
||||
const path = toPath(dirPath);
|
||||
return cachedReadDirectoryResult.has(path) || host.directoryExists(dirPath);
|
||||
}
|
||||
|
||||
function createDirectory(dirPath: string) {
|
||||
const path = toPath(dirPath);
|
||||
const result = getCachedFileSystemEntriesForBaseDir(path);
|
||||
const baseFileName = getBaseNameOfFileName(dirPath);
|
||||
if (result) {
|
||||
updateFileSystemEntry(result.directories, baseFileName, /*isValid*/ true);
|
||||
}
|
||||
host.createDirectory(dirPath);
|
||||
}
|
||||
|
||||
function getDirectories(rootDir: string): string[] {
|
||||
const rootDirPath = toPath(rootDir);
|
||||
const result = tryReadDirectory(rootDir, rootDirPath);
|
||||
if (result) {
|
||||
return result.directories.slice();
|
||||
}
|
||||
return host.getDirectories(rootDir);
|
||||
}
|
||||
|
||||
function readDirectory(rootDir: string, extensions?: ReadonlyArray<string>, excludes?: ReadonlyArray<string>, includes?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
const rootDirPath = toPath(rootDir);
|
||||
const result = tryReadDirectory(rootDir, rootDirPath);
|
||||
if (result) {
|
||||
return matchFiles(rootDir, extensions, excludes, includes, host.useCaseSensitiveFileNames, getCurrentDirectory(), depth, getFileSystemEntries);
|
||||
}
|
||||
return host.readDirectory(rootDir, extensions, excludes, includes, depth);
|
||||
|
||||
function getFileSystemEntries(dir: string) {
|
||||
const path = toPath(dir);
|
||||
if (path === rootDirPath) {
|
||||
return result;
|
||||
}
|
||||
return getCachedFileSystemEntries(path) || createCachedFileSystemEntries(dir, path);
|
||||
}
|
||||
}
|
||||
|
||||
function addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path) {
|
||||
const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath);
|
||||
if (existingResult) {
|
||||
// Just clear the cache for now
|
||||
// For now just clear the cache, since this could mean that multiple level entries might need to be re-evaluated
|
||||
clearCache();
|
||||
}
|
||||
else {
|
||||
// This was earlier a file (hence not in cached directory contents)
|
||||
// or we never cached the directory containing it
|
||||
const parentResult = getCachedFileSystemEntriesForBaseDir(fileOrDirectoryPath);
|
||||
if (parentResult) {
|
||||
const baseName = getBaseNameOfFileName(fileOrDirectory);
|
||||
if (parentResult) {
|
||||
const fsQueryResult: FileAndDirectoryExistence = {
|
||||
fileExists: host.fileExists(fileOrDirectoryPath),
|
||||
directoryExists: host.directoryExists(fileOrDirectoryPath)
|
||||
};
|
||||
if (fsQueryResult.directoryExists || hasEntry(parentResult.directories, baseName)) {
|
||||
// Folder added or removed, clear the cache instead of updating the folder and its structure
|
||||
clearCache();
|
||||
}
|
||||
else {
|
||||
// No need to update the directory structure, just files
|
||||
updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
|
||||
}
|
||||
return fsQueryResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind) {
|
||||
if (eventKind === FileWatcherEventKind.Changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentResult = getCachedFileSystemEntriesForBaseDir(filePath);
|
||||
if (parentResult) {
|
||||
updateFilesOfFileSystemEntry(parentResult, getBaseNameOfFileName(fileName), eventKind === FileWatcherEventKind.Created);
|
||||
}
|
||||
}
|
||||
|
||||
function updateFilesOfFileSystemEntry(parentResult: MutableFileSystemEntries, baseName: string, fileExists: boolean) {
|
||||
updateFileSystemEntry(parentResult.files, baseName, fileExists);
|
||||
}
|
||||
|
||||
function clearCache() {
|
||||
cachedReadDirectoryResult.clear();
|
||||
}
|
||||
export function getBoundFunction<T extends Function>(method: T | undefined, methodOf: {}): T | undefined {
|
||||
return method && method.bind(methodOf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,6 @@ namespace ts {
|
||||
readFile: fileName => sys.readFile(fileName),
|
||||
trace: (s: string) => sys.write(s + newLine),
|
||||
directoryExists: directoryName => sys.directoryExists(directoryName),
|
||||
getEnvironmentVariable: name => sys.getEnvironmentVariable ? sys.getEnvironmentVariable(name) : "",
|
||||
getDirectories: (path: string) => sys.getDirectories(path),
|
||||
realpath
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace ts {
|
||||
onInvalidatedResolution(): void;
|
||||
watchTypeRootsDirectory(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher;
|
||||
onChangedAutomaticTypeDirectiveNames(): void;
|
||||
getCachedDirectoryStructureHost?(): CachedDirectoryStructureHost;
|
||||
getCachedDirectoryStructureHost(): CachedDirectoryStructureHost | undefined;
|
||||
projectName?: string;
|
||||
getGlobalCache?(): string | undefined;
|
||||
writeLog(s: string): void;
|
||||
@@ -87,6 +87,7 @@ namespace ts {
|
||||
const perDirectoryResolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
|
||||
|
||||
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory());
|
||||
const cachedDirectoryStructureHost = resolutionHost.getCachedDirectoryStructureHost();
|
||||
|
||||
/**
|
||||
* These are the extensions that failed lookup files will have by default,
|
||||
@@ -467,9 +468,9 @@ namespace ts {
|
||||
function createDirectoryWatcher(directory: string, dirPath: Path) {
|
||||
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrDirectory => {
|
||||
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
|
||||
if (resolutionHost.getCachedDirectoryStructureHost) {
|
||||
if (cachedDirectoryStructureHost) {
|
||||
// Since the file existance changed, update the sourceFiles cache
|
||||
resolutionHost.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
|
||||
cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
|
||||
}
|
||||
|
||||
// If the files are added to project root or node_modules directory, always run through the invalidation process
|
||||
@@ -596,9 +597,9 @@ namespace ts {
|
||||
// Create new watch and recursive info
|
||||
return resolutionHost.watchTypeRootsDirectory(typeRoot, fileOrDirectory => {
|
||||
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
|
||||
if (resolutionHost.getCachedDirectoryStructureHost) {
|
||||
if (cachedDirectoryStructureHost) {
|
||||
// Since the file existance changed, update the sourceFiles cache
|
||||
resolutionHost.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
|
||||
cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
|
||||
}
|
||||
|
||||
// For now just recompile
|
||||
|
||||
+12
-18
@@ -30,27 +30,14 @@ namespace ts {
|
||||
mtime?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Partial interface of the System thats needed to support the caching of directory structure
|
||||
*/
|
||||
export interface DirectoryStructureHost {
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
fileExists(path: string): boolean;
|
||||
directoryExists(path: string): boolean;
|
||||
createDirectory(path: string): void;
|
||||
getCurrentDirectory(): string;
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
exit(exitCode?: number): void;
|
||||
}
|
||||
|
||||
export interface System extends DirectoryStructureHost {
|
||||
newLine: string;
|
||||
export interface System {
|
||||
args: string[];
|
||||
newLine: string;
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
write(s: string): void;
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
getFileSize?(path: string): number;
|
||||
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
/**
|
||||
* @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that
|
||||
* use native OS file watching
|
||||
@@ -58,7 +45,13 @@ namespace ts {
|
||||
watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
|
||||
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
|
||||
resolvePath(path: string): string;
|
||||
fileExists(path: string): boolean;
|
||||
directoryExists(path: string): boolean;
|
||||
createDirectory(path: string): void;
|
||||
getExecutingFilePath(): string;
|
||||
getCurrentDirectory(): string;
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
getModifiedTime?(path: string): Date;
|
||||
/**
|
||||
* This should be cryptographically secure.
|
||||
@@ -66,6 +59,7 @@ namespace ts {
|
||||
*/
|
||||
createHash?(data: string): string;
|
||||
getMemoryUsage?(): number;
|
||||
exit(exitCode?: number): void;
|
||||
realpath?(path: string): string;
|
||||
/*@internal*/ getEnvironmentVariable(name: string): string;
|
||||
/*@internal*/ tryEnableSourceMapsForHost?(): void;
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ namespace ts {
|
||||
|
||||
const commandLineOptions = commandLine.options;
|
||||
if (configFileName) {
|
||||
const configParseResult = parseConfigFile(configFileName, commandLineOptions, sys, reportDiagnostic);
|
||||
const configParseResult = parseConfigFileWithSystem(configFileName, commandLineOptions, sys, reportDiagnostic);
|
||||
udpateReportDiagnostic(configParseResult.options);
|
||||
if (isWatchSet(configParseResult.options)) {
|
||||
reportWatchModeWithoutSysSupport();
|
||||
|
||||
@@ -4327,7 +4327,6 @@ namespace ts {
|
||||
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
|
||||
*/
|
||||
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
|
||||
getEnvironmentVariable?(name: string): string;
|
||||
/* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions): void;
|
||||
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
|
||||
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
|
||||
|
||||
+138
-49
@@ -33,34 +33,52 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors
|
||||
*/
|
||||
/*@internal*/
|
||||
export interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter {
|
||||
getCurrentDirectory(): string;
|
||||
}
|
||||
|
||||
/** Parses config file using System interface */
|
||||
/*@internal*/
|
||||
export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) {
|
||||
const host: ParseConfigFileHost = <any>system;
|
||||
host.onConfigFileDiagnostic = reportDiagnostic;
|
||||
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(sys, reportDiagnostic, diagnostic);
|
||||
const result = parseConfigFile(configFileName, optionsToExtend, host);
|
||||
host.onConfigFileDiagnostic = undefined;
|
||||
host.onUnRecoverableConfigFileDiagnostic = undefined;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the config file, reports errors if any and exits if the config file cannot be found
|
||||
*/
|
||||
/*@internal*/
|
||||
export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, system: DirectoryStructureHost, reportDiagnostic: DiagnosticReporter): ParsedCommandLine {
|
||||
export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined {
|
||||
let configFileText: string;
|
||||
try {
|
||||
configFileText = system.readFile(configFileName);
|
||||
configFileText = host.readFile(configFileName);
|
||||
}
|
||||
catch (e) {
|
||||
const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message);
|
||||
reportDiagnostic(error);
|
||||
system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
return;
|
||||
host.onUnRecoverableConfigFileDiagnostic(error);
|
||||
return undefined;
|
||||
}
|
||||
if (!configFileText) {
|
||||
const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName);
|
||||
reportDiagnostic(error);
|
||||
system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
return;
|
||||
host.onUnRecoverableConfigFileDiagnostic(error);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = parseJsonText(configFileName, configFileText);
|
||||
result.parseDiagnostics.forEach(reportDiagnostic);
|
||||
result.parseDiagnostics.forEach(diagnostic => host.onConfigFileDiagnostic(diagnostic));
|
||||
|
||||
const cwd = system.getCurrentDirectory();
|
||||
const configParseResult = parseJsonSourceFileConfigFileContent(result, system, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
|
||||
configParseResult.errors.forEach(reportDiagnostic);
|
||||
const cwd = host.getCurrentDirectory();
|
||||
const configParseResult = parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd));
|
||||
configParseResult.errors.forEach(diagnostic => host.onConfigFileDiagnostic(diagnostic));
|
||||
|
||||
return configParseResult;
|
||||
}
|
||||
@@ -90,7 +108,7 @@ namespace ts {
|
||||
reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system);
|
||||
const builder = createEmitAndSemanticDiagnosticsBuilder({
|
||||
getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames),
|
||||
computeHash: system.createHash ? system.createHash.bind(system) : identity
|
||||
computeHash: getBoundFunction(system.createHash, system) || identity
|
||||
});
|
||||
|
||||
return (host: DirectoryStructureHost, program: Program) => {
|
||||
@@ -173,8 +191,30 @@ namespace ts {
|
||||
// Sub set of compiler host methods to read and generate new program
|
||||
useCaseSensitiveFileNames(): boolean;
|
||||
getNewLine(): string;
|
||||
getCurrentDirectory(): string;
|
||||
|
||||
/** If provided this function would be used to resolve the module names, otherwise typescript's default module resolution */
|
||||
/**
|
||||
* Use to check file presence for source files and
|
||||
* if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well
|
||||
*/
|
||||
fileExists(path: string): boolean;
|
||||
/**
|
||||
* Use to read file text for source files and
|
||||
* if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well
|
||||
*/
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
|
||||
/** If provided, used for module resolution as well as to handle directory structure */
|
||||
directoryExists?(path: string): boolean;
|
||||
/** If provided, used in resolutions as well as handling directory structure */
|
||||
getDirectories?(path: string): string[];
|
||||
/** If provided, used to cache and handle directory structure modifications */
|
||||
readDirectory?(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
|
||||
/** Symbol links resolution */
|
||||
realpath?(path: string): string;
|
||||
|
||||
/** If provided, used to resolve the module names, otherwise typescript's default module resolution */
|
||||
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
|
||||
}
|
||||
|
||||
@@ -189,18 +229,36 @@ namespace ts {
|
||||
options: CompilerOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports config file diagnostics
|
||||
*/
|
||||
export interface ConfigFileDiagnosticsReporter {
|
||||
/**
|
||||
* Reports the diagnostics in reading/writing or parsing of the config file
|
||||
*/
|
||||
onConfigFileDiagnostic(diagnostic: Diagnostic): void;
|
||||
|
||||
/**
|
||||
* Reports unrecoverable error when parsing config file
|
||||
*/
|
||||
onUnRecoverableConfigFileDiagnostic(diagnostic: Diagnostic): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Host to create watch with config file
|
||||
*/
|
||||
export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost {
|
||||
export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost, ConfigFileDiagnosticsReporter {
|
||||
/** Name of the config file to compile */
|
||||
configFileName: string;
|
||||
|
||||
/** Options to extend */
|
||||
optionsToExtend?: CompilerOptions;
|
||||
|
||||
// Reports errors in the config file
|
||||
onConfigFileDiagnostic(diagnostic: Diagnostic): void;
|
||||
/**
|
||||
* Used to generate source file names from the config file and its include, exclude, files rules
|
||||
* and also to cache the directory stucture
|
||||
*/
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,21 +299,39 @@ namespace ts {
|
||||
* Creates the watch compiler host that can be extended with config file or root file names and options host
|
||||
*/
|
||||
/*@internal*/
|
||||
export function createWatchCompilerHost(system = sys, reportDiagnostic?: DiagnosticReporter): WatchCompilerHost {
|
||||
export function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHost {
|
||||
return {
|
||||
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
|
||||
getNewLine: () => system.newLine,
|
||||
getCurrentDirectory: getBoundFunction(system.getCurrentDirectory, system),
|
||||
fileExists: getBoundFunction(system.fileExists, system),
|
||||
readFile: getBoundFunction(system.readFile, system),
|
||||
directoryExists: getBoundFunction(system.directoryExists, system),
|
||||
getDirectories: getBoundFunction(system.getDirectories, system),
|
||||
readDirectory: getBoundFunction(system.readDirectory, system),
|
||||
realpath: getBoundFunction(system.realpath, system),
|
||||
system,
|
||||
afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Report error and exit
|
||||
*/
|
||||
/*@internal*/
|
||||
export function reportUnrecoverableDiagnostic(system: System, reportDiagnostic: DiagnosticReporter, diagnostic: Diagnostic) {
|
||||
reportDiagnostic(diagnostic);
|
||||
system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the watched program for config file
|
||||
*/
|
||||
export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile {
|
||||
const host = createWatchCompilerHost(system) as WatchCompilerHostOfConfigFile;
|
||||
host.onConfigFileDiagnostic = reportDiagnostic || createDiagnosticReporter(system);
|
||||
reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system);
|
||||
const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfConfigFile;
|
||||
host.onConfigFileDiagnostic = reportDiagnostic;
|
||||
host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic);
|
||||
host.configFileName = configFileName;
|
||||
host.optionsToExtend = optionsToExtend;
|
||||
return createWatch(host);
|
||||
@@ -279,7 +355,7 @@ namespace ts {
|
||||
* Creates the watch from the host for config file
|
||||
*/
|
||||
export function createWatch(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile;
|
||||
export function createWatch(host: WatchCompilerHostOfFilesAndCompilerOptions | WatchCompilerHostOfConfigFile): WatchOfFilesAndCompilerOptions | WatchOfConfigFile {
|
||||
export function createWatch(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile): WatchOfFilesAndCompilerOptions | WatchOfConfigFile {
|
||||
interface HostFileInfo {
|
||||
version: number;
|
||||
sourceFile: SourceFile;
|
||||
@@ -297,13 +373,30 @@ namespace ts {
|
||||
let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations
|
||||
let hasChangedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed
|
||||
|
||||
const { system, configFileName, onConfigFileDiagnostic, optionsToExtend: optionsToExtendForConfigFile = {} } = host as WatchCompilerHostOfConfigFile;
|
||||
const beforeProgramCreate: WatchCompilerHost["beforeProgramCreate"] = host.beforeProgramCreate ? host.beforeProgramCreate.bind(host) : noop;
|
||||
const afterProgramCreate: WatchCompilerHost["afterProgramCreate"] = host.afterProgramCreate ? host.afterProgramCreate.bind(host) : noop;
|
||||
let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host as WatchCompilerHostOfConfigFile;
|
||||
const system = host.system;
|
||||
const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames();
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
const getCurrentDirectory = () => currentDirectory;
|
||||
const onConfigFileDiagnostic = getBoundFunction(host.onConfigFileDiagnostic, host);
|
||||
const readFile = getBoundFunction(host.readFile, host);
|
||||
const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {} } = host;
|
||||
const beforeProgramCreate: WatchCompilerHost["beforeProgramCreate"] = getBoundFunction(host.beforeProgramCreate, host) || noop;
|
||||
const afterProgramCreate: WatchCompilerHost["afterProgramCreate"] = getBoundFunction(host.afterProgramCreate, host) || noop;
|
||||
let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host;
|
||||
|
||||
const cachedDirectoryStructureHost = configFileName && createCachedDirectoryStructureHost(host, currentDirectory, useCaseSensitiveFileNames);
|
||||
const directoryStructureHost: DirectoryStructureHost = cachedDirectoryStructureHost || host;
|
||||
const parseConfigFileHost: ParseConfigFileHost = {
|
||||
useCaseSensitiveFileNames,
|
||||
readDirectory: getBoundFunction(directoryStructureHost.readDirectory, directoryStructureHost),
|
||||
fileExists: getBoundFunction(directoryStructureHost.fileExists, directoryStructureHost),
|
||||
readFile,
|
||||
getCurrentDirectory,
|
||||
onConfigFileDiagnostic,
|
||||
onUnRecoverableConfigFileDiagnostic: getBoundFunction(host.onUnRecoverableConfigFileDiagnostic, host)
|
||||
};
|
||||
|
||||
// From tsc we want to get already parsed result and hence check for rootFileNames
|
||||
const directoryStructureHost = configFileName ? createCachedDirectoryStructureHost(system) : system;
|
||||
if (configFileName && !rootFileNames) {
|
||||
parseConfigFile();
|
||||
}
|
||||
@@ -318,11 +411,7 @@ namespace ts {
|
||||
watchFile(system, configFileName, scheduleProgramReload, writeLog);
|
||||
}
|
||||
|
||||
const getCurrentDirectory = memoize(() => directoryStructureHost.getCurrentDirectory());
|
||||
const realpath = system.realpath && ((path: string) => system.realpath(path));
|
||||
const getCachedDirectoryStructureHost = configFileName && (() => directoryStructureHost as CachedDirectoryStructureHost);
|
||||
const useCaseSensitiveFileNames = memoize(() => host.useCaseSensitiveFileNames());
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames());
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
let newLine = updateNewLine();
|
||||
|
||||
const compilerHost: CompilerHost & ResolutionCacheHost = {
|
||||
@@ -333,23 +422,22 @@ namespace ts {
|
||||
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
|
||||
writeFile: notImplemented,
|
||||
getCurrentDirectory,
|
||||
useCaseSensitiveFileNames,
|
||||
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
||||
getCanonicalFileName,
|
||||
getNewLine: () => newLine,
|
||||
fileExists,
|
||||
readFile: fileName => system.readFile(fileName),
|
||||
readFile,
|
||||
trace: s => system.write(s + newLine),
|
||||
directoryExists: directoryName => directoryStructureHost.directoryExists(directoryName),
|
||||
getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
|
||||
getDirectories: path => directoryStructureHost.getDirectories(path),
|
||||
realpath,
|
||||
directoryExists: getBoundFunction(directoryStructureHost.directoryExists, directoryStructureHost),
|
||||
getDirectories: getBoundFunction(directoryStructureHost.getDirectories, directoryStructureHost),
|
||||
realpath: getBoundFunction(host.realpath, host),
|
||||
onReleaseOldSourceFile,
|
||||
// Members for ResolutionCacheHost
|
||||
toPath,
|
||||
getCompilationSettings: () => compilerOptions,
|
||||
watchDirectoryOfFailedLookupLocation: watchDirectory,
|
||||
watchTypeRootsDirectory: watchDirectory,
|
||||
getCachedDirectoryStructureHost,
|
||||
getCachedDirectoryStructureHost: () => cachedDirectoryStructureHost,
|
||||
onInvalidatedResolution: scheduleProgramUpdate,
|
||||
onChangedAutomaticTypeDirectiveNames: () => {
|
||||
hasChangedAutomaticTypeDirectiveNames = true;
|
||||
@@ -359,8 +447,8 @@ namespace ts {
|
||||
};
|
||||
// Cache for the module resolution
|
||||
const resolutionCache = createResolutionCache(compilerHost, configFileName ?
|
||||
getDirectoryPath(getNormalizedAbsolutePath(configFileName, getCurrentDirectory())) :
|
||||
getCurrentDirectory(),
|
||||
getDirectoryPath(getNormalizedAbsolutePath(configFileName, currentDirectory)) :
|
||||
currentDirectory,
|
||||
/*logChangesWhenResolvingModule*/ false
|
||||
);
|
||||
// Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names
|
||||
@@ -442,7 +530,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function toPath(fileName: string) {
|
||||
return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName);
|
||||
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
}
|
||||
|
||||
function fileExists(fileName: string) {
|
||||
@@ -505,7 +593,7 @@ namespace ts {
|
||||
let text: string;
|
||||
try {
|
||||
performance.mark("beforeIORead");
|
||||
text = system.readFile(fileName, compilerOptions.charset);
|
||||
text = host.readFile(fileName, compilerOptions.charset);
|
||||
performance.mark("afterIORead");
|
||||
performance.measure("I/O Read", "beforeIORead", "afterIORead");
|
||||
}
|
||||
@@ -601,7 +689,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function reloadFileNamesFromConfigFile() {
|
||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, directoryStructureHost);
|
||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, parseConfigFileHost);
|
||||
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
|
||||
onConfigFileDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
|
||||
}
|
||||
@@ -615,8 +703,9 @@ namespace ts {
|
||||
writeLog(`Reloading config file: ${configFileName}`);
|
||||
reloadLevel = ConfigFileProgramReloadLevel.None;
|
||||
|
||||
const cachedHost = directoryStructureHost as CachedDirectoryStructureHost;
|
||||
cachedHost.clearCache();
|
||||
if (cachedDirectoryStructureHost) {
|
||||
cachedDirectoryStructureHost.clearCache();
|
||||
}
|
||||
parseConfigFile();
|
||||
hasChangedCompilerOptions = true;
|
||||
synchronizeProgram();
|
||||
@@ -626,7 +715,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseConfigFile() {
|
||||
const configParseResult = ts.parseConfigFile(configFileName, optionsToExtendForConfigFile, directoryStructureHost as CachedDirectoryStructureHost, onConfigFileDiagnostic);
|
||||
const configParseResult = ts.parseConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost);
|
||||
rootFileNames = configParseResult.fileNames;
|
||||
compilerOptions = configParseResult.options;
|
||||
configFileSpecs = configParseResult.configFileSpecs;
|
||||
@@ -662,8 +751,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function updateCachedSystemWithFile(fileName: string, path: Path, eventKind: FileWatcherEventKind) {
|
||||
if (configFileName) {
|
||||
(directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFile(fileName, path, eventKind);
|
||||
if (cachedDirectoryStructureHost) {
|
||||
cachedDirectoryStructureHost.addOrDeleteFile(fileName, path, eventKind);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,7 +801,7 @@ namespace ts {
|
||||
const fileOrDirectoryPath = toPath(fileOrDirectory);
|
||||
|
||||
// Since the file existance changed, update the sourceFiles cache
|
||||
const result = (directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
|
||||
const result = cachedDirectoryStructureHost && cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
|
||||
|
||||
// Instead of deleting the file, mark it as changed instead
|
||||
// Many times node calls add/remove/file when watching directories recursively
|
||||
|
||||
@@ -2,6 +2,247 @@
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
/**
|
||||
* Partial interface of the System thats needed to support the caching of directory structure
|
||||
*/
|
||||
export interface DirectoryStructureHost {
|
||||
fileExists(path: string): boolean;
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
|
||||
directoryExists?(path: string): boolean;
|
||||
getDirectories?(path: string): string[];
|
||||
readDirectory?(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
|
||||
createDirectory?(path: string): void;
|
||||
writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
}
|
||||
|
||||
interface FileAndDirectoryExistence {
|
||||
fileExists: boolean;
|
||||
directoryExists: boolean;
|
||||
}
|
||||
|
||||
export interface CachedDirectoryStructureHost extends DirectoryStructureHost {
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
|
||||
/** Returns the queried result for the file exists and directory exists if at all it was done */
|
||||
addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): FileAndDirectoryExistence | undefined;
|
||||
addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind): void;
|
||||
clearCache(): void;
|
||||
}
|
||||
|
||||
interface MutableFileSystemEntries {
|
||||
readonly files: string[];
|
||||
readonly directories: string[];
|
||||
}
|
||||
|
||||
export function createCachedDirectoryStructureHost(host: DirectoryStructureHost, currentDirectory: string, useCaseSensitiveFileNames: boolean): CachedDirectoryStructureHost | undefined {
|
||||
if (!host.getDirectories || !host.readDirectory) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cachedReadDirectoryResult = createMap<MutableFileSystemEntries>();
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
return {
|
||||
useCaseSensitiveFileNames,
|
||||
fileExists,
|
||||
readFile: getBoundFunction(host.readFile, host),
|
||||
directoryExists: host.directoryExists && directoryExists,
|
||||
getDirectories,
|
||||
readDirectory,
|
||||
createDirectory,
|
||||
writeFile,
|
||||
addOrDeleteFileOrDirectory,
|
||||
addOrDeleteFile,
|
||||
clearCache
|
||||
};
|
||||
|
||||
function toPath(fileName: string) {
|
||||
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
}
|
||||
|
||||
function getCachedFileSystemEntries(rootDirPath: Path): MutableFileSystemEntries | undefined {
|
||||
return cachedReadDirectoryResult.get(rootDirPath);
|
||||
}
|
||||
|
||||
function getCachedFileSystemEntriesForBaseDir(path: Path): MutableFileSystemEntries | undefined {
|
||||
return getCachedFileSystemEntries(getDirectoryPath(path));
|
||||
}
|
||||
|
||||
function getBaseNameOfFileName(fileName: string) {
|
||||
return getBaseFileName(normalizePath(fileName));
|
||||
}
|
||||
|
||||
function createCachedFileSystemEntries(rootDir: string, rootDirPath: Path) {
|
||||
const resultFromHost: MutableFileSystemEntries = {
|
||||
files: map(host.readDirectory(rootDir, /*extensions*/ undefined, /*exclude*/ undefined, /*include*/["*.*"]), getBaseNameOfFileName) || [],
|
||||
directories: host.getDirectories(rootDir) || []
|
||||
};
|
||||
|
||||
cachedReadDirectoryResult.set(rootDirPath, resultFromHost);
|
||||
return resultFromHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the readDirectory result was already cached, it returns that
|
||||
* Otherwise gets result from host and caches it.
|
||||
* The host request is done under try catch block to avoid caching incorrect result
|
||||
*/
|
||||
function tryReadDirectory(rootDir: string, rootDirPath: Path): MutableFileSystemEntries | undefined {
|
||||
const cachedResult = getCachedFileSystemEntries(rootDirPath);
|
||||
if (cachedResult) {
|
||||
return cachedResult;
|
||||
}
|
||||
|
||||
try {
|
||||
return createCachedFileSystemEntries(rootDir, rootDirPath);
|
||||
}
|
||||
catch (_e) {
|
||||
// If there is exception to read directories, dont cache the result and direct the calls to host
|
||||
Debug.assert(!cachedReadDirectoryResult.has(rootDirPath));
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function fileNameEqual(name1: string, name2: string) {
|
||||
return getCanonicalFileName(name1) === getCanonicalFileName(name2);
|
||||
}
|
||||
|
||||
function hasEntry(entries: ReadonlyArray<string>, name: string) {
|
||||
return some(entries, file => fileNameEqual(file, name));
|
||||
}
|
||||
|
||||
function updateFileSystemEntry(entries: string[], baseName: string, isValid: boolean) {
|
||||
if (hasEntry(entries, baseName)) {
|
||||
if (!isValid) {
|
||||
return filterMutate(entries, entry => !fileNameEqual(entry, baseName));
|
||||
}
|
||||
}
|
||||
else if (isValid) {
|
||||
return entries.push(baseName);
|
||||
}
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void {
|
||||
const path = toPath(fileName);
|
||||
const result = getCachedFileSystemEntriesForBaseDir(path);
|
||||
if (result) {
|
||||
updateFilesOfFileSystemEntry(result, getBaseNameOfFileName(fileName), /*fileExists*/ true);
|
||||
}
|
||||
return host.writeFile(fileName, data, writeByteOrderMark);
|
||||
}
|
||||
|
||||
function fileExists(fileName: string): boolean {
|
||||
const path = toPath(fileName);
|
||||
const result = getCachedFileSystemEntriesForBaseDir(path);
|
||||
return result && hasEntry(result.files, getBaseNameOfFileName(fileName)) ||
|
||||
host.fileExists(fileName);
|
||||
}
|
||||
|
||||
function directoryExists(dirPath: string): boolean {
|
||||
const path = toPath(dirPath);
|
||||
return cachedReadDirectoryResult.has(path) || host.directoryExists(dirPath);
|
||||
}
|
||||
|
||||
function createDirectory(dirPath: string) {
|
||||
const path = toPath(dirPath);
|
||||
const result = getCachedFileSystemEntriesForBaseDir(path);
|
||||
const baseFileName = getBaseNameOfFileName(dirPath);
|
||||
if (result) {
|
||||
updateFileSystemEntry(result.directories, baseFileName, /*isValid*/ true);
|
||||
}
|
||||
host.createDirectory(dirPath);
|
||||
}
|
||||
|
||||
function getDirectories(rootDir: string): string[] {
|
||||
const rootDirPath = toPath(rootDir);
|
||||
const result = tryReadDirectory(rootDir, rootDirPath);
|
||||
if (result) {
|
||||
return result.directories.slice();
|
||||
}
|
||||
return host.getDirectories(rootDir);
|
||||
}
|
||||
|
||||
function readDirectory(rootDir: string, extensions?: ReadonlyArray<string>, excludes?: ReadonlyArray<string>, includes?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
const rootDirPath = toPath(rootDir);
|
||||
const result = tryReadDirectory(rootDir, rootDirPath);
|
||||
if (result) {
|
||||
return matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames, currentDirectory, depth, getFileSystemEntries);
|
||||
}
|
||||
return host.readDirectory(rootDir, extensions, excludes, includes, depth);
|
||||
|
||||
function getFileSystemEntries(dir: string) {
|
||||
const path = toPath(dir);
|
||||
if (path === rootDirPath) {
|
||||
return result;
|
||||
}
|
||||
return getCachedFileSystemEntries(path) || createCachedFileSystemEntries(dir, path);
|
||||
}
|
||||
}
|
||||
|
||||
function addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path) {
|
||||
const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath);
|
||||
if (existingResult) {
|
||||
// Just clear the cache for now
|
||||
// For now just clear the cache, since this could mean that multiple level entries might need to be re-evaluated
|
||||
clearCache();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parentResult = getCachedFileSystemEntriesForBaseDir(fileOrDirectoryPath);
|
||||
if (!parentResult) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// This was earlier a file (hence not in cached directory contents)
|
||||
// or we never cached the directory containing it
|
||||
|
||||
if (!host.directoryExists) {
|
||||
// Since host doesnt support directory exists, clear the cache as otherwise it might not be same
|
||||
clearCache();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const baseName = getBaseNameOfFileName(fileOrDirectory);
|
||||
const fsQueryResult: FileAndDirectoryExistence = {
|
||||
fileExists: host.fileExists(fileOrDirectoryPath),
|
||||
directoryExists: host.directoryExists(fileOrDirectoryPath)
|
||||
};
|
||||
if (fsQueryResult.directoryExists || hasEntry(parentResult.directories, baseName)) {
|
||||
// Folder added or removed, clear the cache instead of updating the folder and its structure
|
||||
clearCache();
|
||||
}
|
||||
else {
|
||||
// No need to update the directory structure, just files
|
||||
updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
|
||||
}
|
||||
return fsQueryResult;
|
||||
|
||||
}
|
||||
|
||||
function addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind) {
|
||||
if (eventKind === FileWatcherEventKind.Changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentResult = getCachedFileSystemEntriesForBaseDir(filePath);
|
||||
if (parentResult) {
|
||||
updateFilesOfFileSystemEntry(parentResult, getBaseNameOfFileName(fileName), eventKind === FileWatcherEventKind.Created);
|
||||
}
|
||||
}
|
||||
|
||||
function updateFilesOfFileSystemEntry(parentResult: MutableFileSystemEntries, baseName: string, fileExists: boolean) {
|
||||
updateFileSystemEntry(parentResult.files, baseName, fileExists);
|
||||
}
|
||||
|
||||
function clearCache() {
|
||||
cachedReadDirectoryResult.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export enum ConfigFileProgramReloadLevel {
|
||||
None,
|
||||
/** Update the file name list from the disk */
|
||||
|
||||
@@ -903,7 +903,7 @@ namespace ts {
|
||||
|
||||
function verifyProgramWithConfigFile(system: System, configFileName: string) {
|
||||
const program = createWatchOfConfigFile(configFileName, {}, system).getExistingProgram();
|
||||
const { fileNames, options } = parseConfigFile(configFileName, {}, system, notImplemented);
|
||||
const { fileNames, options } = parseConfigFileWithSystem(configFileName, {}, system, notImplemented);
|
||||
verifyProgramIsUptoDate(program, fileNames, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -1497,7 +1497,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private createConfiguredProject(configFileName: NormalizedPath) {
|
||||
const cachedDirectoryStructureHost = createCachedDirectoryStructureHost(this.host);
|
||||
const cachedDirectoryStructureHost = createCachedDirectoryStructureHost(this.host, this.host.getCurrentDirectory(), this.host.useCaseSensitiveFileNames);
|
||||
const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedDirectoryStructureHost);
|
||||
this.logger.info(`Opened configuration file ${configFileName}`);
|
||||
const languageServiceEnabled = !this.exceededTotalSizeLimitForNonTsFiles(configFileName, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader);
|
||||
|
||||
@@ -334,7 +334,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
useCaseSensitiveFileNames() {
|
||||
return this.directoryStructureHost.useCaseSensitiveFileNames;
|
||||
return this.projectService.host.useCaseSensitiveFileNames;
|
||||
}
|
||||
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
@@ -342,7 +342,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
readFile(fileName: string): string | undefined {
|
||||
return this.directoryStructureHost.readFile(fileName);
|
||||
return this.projectService.host.readFile(fileName);
|
||||
}
|
||||
|
||||
fileExists(file: string): boolean {
|
||||
@@ -368,6 +368,11 @@ namespace ts.server {
|
||||
return this.directoryStructureHost.getDirectories(path);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getCachedDirectoryStructureHost(): CachedDirectoryStructureHost {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
toPath(fileName: string) {
|
||||
return toPath(fileName, this.currentDirectory, this.projectService.toCanonicalFileName);
|
||||
@@ -877,7 +882,7 @@ namespace ts.server {
|
||||
missingFilePath,
|
||||
(fileName, eventKind) => {
|
||||
if (this.projectKind === ProjectKind.Configured) {
|
||||
(this.directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFile(fileName, missingFilePath, eventKind);
|
||||
this.getCachedDirectoryStructureHost().addOrDeleteFile(fileName, missingFilePath, eventKind);
|
||||
}
|
||||
|
||||
if (eventKind === FileWatcherEventKind.Created && this.missingFilesMap.has(missingFilePath)) {
|
||||
|
||||
@@ -313,7 +313,7 @@ namespace ts.server {
|
||||
detachAllProjects() {
|
||||
for (const p of this.containingProjects) {
|
||||
if (p.projectKind === ProjectKind.Configured) {
|
||||
(p.directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFile(this.fileName, this.path, FileWatcherEventKind.Deleted);
|
||||
p.getCachedDirectoryStructureHost().addOrDeleteFile(this.fileName, this.path, FileWatcherEventKind.Deleted);
|
||||
}
|
||||
const isInfoRoot = p.isRoot(this);
|
||||
// detach is unnecessary since we'll clean the list of containing projects anyways
|
||||
|
||||
+12
-18
@@ -2477,7 +2477,6 @@ declare namespace ts {
|
||||
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
|
||||
*/
|
||||
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
|
||||
getEnvironmentVariable?(name: string): string;
|
||||
}
|
||||
interface SourceMapRange extends TextRange {
|
||||
source?: SourceMapSource;
|
||||
@@ -2725,26 +2724,14 @@ declare namespace ts {
|
||||
callback: FileWatcherCallback;
|
||||
mtime?: Date;
|
||||
}
|
||||
/**
|
||||
* Partial interface of the System thats needed to support the caching of directory structure
|
||||
*/
|
||||
interface DirectoryStructureHost {
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
fileExists(path: string): boolean;
|
||||
directoryExists(path: string): boolean;
|
||||
createDirectory(path: string): void;
|
||||
getCurrentDirectory(): string;
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
exit(exitCode?: number): void;
|
||||
}
|
||||
interface System extends DirectoryStructureHost {
|
||||
newLine: string;
|
||||
interface System {
|
||||
args: string[];
|
||||
newLine: string;
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
write(s: string): void;
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
getFileSize?(path: string): number;
|
||||
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
/**
|
||||
* @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that
|
||||
* use native OS file watching
|
||||
@@ -2752,7 +2739,13 @@ declare namespace ts {
|
||||
watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
|
||||
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
|
||||
resolvePath(path: string): string;
|
||||
fileExists(path: string): boolean;
|
||||
directoryExists(path: string): boolean;
|
||||
createDirectory(path: string): void;
|
||||
getExecutingFilePath(): string;
|
||||
getCurrentDirectory(): string;
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
getModifiedTime?(path: string): Date;
|
||||
/**
|
||||
* This should be cryptographically secure.
|
||||
@@ -2760,6 +2753,7 @@ declare namespace ts {
|
||||
*/
|
||||
createHash?(data: string): string;
|
||||
getMemoryUsage?(): number;
|
||||
exit(exitCode?: number): void;
|
||||
realpath?(path: string): string;
|
||||
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
|
||||
clearTimeout?(timeoutId: any): void;
|
||||
|
||||
+51
-21
@@ -2477,7 +2477,6 @@ declare namespace ts {
|
||||
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
|
||||
*/
|
||||
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
|
||||
getEnvironmentVariable?(name: string): string;
|
||||
}
|
||||
interface SourceMapRange extends TextRange {
|
||||
source?: SourceMapSource;
|
||||
@@ -2725,26 +2724,14 @@ declare namespace ts {
|
||||
callback: FileWatcherCallback;
|
||||
mtime?: Date;
|
||||
}
|
||||
/**
|
||||
* Partial interface of the System thats needed to support the caching of directory structure
|
||||
*/
|
||||
interface DirectoryStructureHost {
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
fileExists(path: string): boolean;
|
||||
directoryExists(path: string): boolean;
|
||||
createDirectory(path: string): void;
|
||||
getCurrentDirectory(): string;
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
exit(exitCode?: number): void;
|
||||
}
|
||||
interface System extends DirectoryStructureHost {
|
||||
newLine: string;
|
||||
interface System {
|
||||
args: string[];
|
||||
newLine: string;
|
||||
useCaseSensitiveFileNames: boolean;
|
||||
write(s: string): void;
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
getFileSize?(path: string): number;
|
||||
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
|
||||
/**
|
||||
* @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that
|
||||
* use native OS file watching
|
||||
@@ -2752,7 +2739,13 @@ declare namespace ts {
|
||||
watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
|
||||
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
|
||||
resolvePath(path: string): string;
|
||||
fileExists(path: string): boolean;
|
||||
directoryExists(path: string): boolean;
|
||||
createDirectory(path: string): void;
|
||||
getExecutingFilePath(): string;
|
||||
getCurrentDirectory(): string;
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
getModifiedTime?(path: string): Date;
|
||||
/**
|
||||
* This should be cryptographically secure.
|
||||
@@ -2760,6 +2753,7 @@ declare namespace ts {
|
||||
*/
|
||||
createHash?(data: string): string;
|
||||
getMemoryUsage?(): number;
|
||||
exit(exitCode?: number): void;
|
||||
realpath?(path: string): string;
|
||||
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
|
||||
clearTimeout?(timeoutId: any): void;
|
||||
@@ -3829,7 +3823,26 @@ declare namespace ts {
|
||||
afterProgramCreate?(host: DirectoryStructureHost, program: Program): void;
|
||||
useCaseSensitiveFileNames(): boolean;
|
||||
getNewLine(): string;
|
||||
/** If provided this function would be used to resolve the module names, otherwise typescript's default module resolution */
|
||||
getCurrentDirectory(): string;
|
||||
/**
|
||||
* Use to check file presence for source files and
|
||||
* if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well
|
||||
*/
|
||||
fileExists(path: string): boolean;
|
||||
/**
|
||||
* Use to read file text for source files and
|
||||
* if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well
|
||||
*/
|
||||
readFile(path: string, encoding?: string): string | undefined;
|
||||
/** If provided, used for module resolution as well as to handle directory structure */
|
||||
directoryExists?(path: string): boolean;
|
||||
/** If provided, used in resolutions as well as handling directory structure */
|
||||
getDirectories?(path: string): string[];
|
||||
/** If provided, used to cache and handle directory structure modifications */
|
||||
readDirectory?(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
/** Symbol links resolution */
|
||||
realpath?(path: string): string;
|
||||
/** If provided, used to resolve the module names, otherwise typescript's default module resolution */
|
||||
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
|
||||
}
|
||||
/**
|
||||
@@ -3841,15 +3854,32 @@ declare namespace ts {
|
||||
/** Compiler options */
|
||||
options: CompilerOptions;
|
||||
}
|
||||
/**
|
||||
* Reports config file diagnostics
|
||||
*/
|
||||
interface ConfigFileDiagnosticsReporter {
|
||||
/**
|
||||
* Reports the diagnostics in reading/writing or parsing of the config file
|
||||
*/
|
||||
onConfigFileDiagnostic(diagnostic: Diagnostic): void;
|
||||
/**
|
||||
* Reports unrecoverable error when parsing config file
|
||||
*/
|
||||
onUnRecoverableConfigFileDiagnostic(diagnostic: Diagnostic): void;
|
||||
}
|
||||
/**
|
||||
* Host to create watch with config file
|
||||
*/
|
||||
interface WatchCompilerHostOfConfigFile extends WatchCompilerHost {
|
||||
interface WatchCompilerHostOfConfigFile extends WatchCompilerHost, ConfigFileDiagnosticsReporter {
|
||||
/** Name of the config file to compile */
|
||||
configFileName: string;
|
||||
/** Options to extend */
|
||||
optionsToExtend?: CompilerOptions;
|
||||
onConfigFileDiagnostic(diagnostic: Diagnostic): void;
|
||||
/**
|
||||
* Used to generate source file names from the config file and its include, exclude, files rules
|
||||
* and also to cache the directory stucture
|
||||
*/
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
|
||||
}
|
||||
interface Watch {
|
||||
/** Synchronize with host and get updated program */
|
||||
|
||||
Reference in New Issue
Block a user