Create composite host/fshost.

This removes fshost usage in most places except for construction of
objects.
This commit is contained in:
Nathan Shively-Sanders
2022-06-08 10:03:19 -07:00
parent a29c27ce47
commit 2ba27a58fb
13 changed files with 167 additions and 104 deletions
+2 -2
View File
@@ -918,7 +918,7 @@ namespace Harness.LanguageService {
const proxy = makeDefaultProxy(info);
proxy.getSemanticDiagnostics = filename => {
const prev = info.languageService.getSemanticDiagnostics(filename);
const sourceFile: ts.SourceFile = info.project.getSourceFile(ts.toPath(filename, /*basePath*/ undefined, ts.createGetCanonicalFileName(info.fsHost.useCaseSensitiveFileNames)))!;
const sourceFile: ts.SourceFile = info.project.getSourceFile(ts.toPath(filename, /*basePath*/ undefined, ts.createGetCanonicalFileName(info.serverHost.useCaseSensitiveFileNames)))!;
prev.push({
category: ts.DiagnosticCategory.Warning,
file: sourceFile,
@@ -945,7 +945,7 @@ namespace Harness.LanguageService {
const proxy = makeDefaultProxy(info);
proxy.getSemanticDiagnostics = filename => {
const prev = info.languageService.getSemanticDiagnostics(filename);
const sourceFile: ts.SourceFile = info.project.getSourceFile(ts.toPath(filename, /*basePath*/ undefined, ts.createGetCanonicalFileName(info.fsHost.useCaseSensitiveFileNames)))!;
const sourceFile: ts.SourceFile = info.project.getSourceFile(ts.toPath(filename, /*basePath*/ undefined, ts.createGetCanonicalFileName(info.serverHost.useCaseSensitiveFileNames)))!;
prev.push({
category: ts.DiagnosticCategory.Error,
file: sourceFile,
+100 -39
View File
@@ -358,6 +358,67 @@ namespace ts.server {
}
}
/*@internal*/
export function getFileSystemHost(host: ServerHost, fshost: FileServerHost | undefined): ServerHost {
return !fshost ? host
: {
// FileServerHost
readFile: fshost.readFile.bind(fshost),
writeFile: fshost.writeFile.bind(fshost),
fileExists: fshost.fileExists.bind(fshost),
directoryExists: fshost.directoryExists.bind(fshost),
getFileSize: fshost.getFileSize?.bind(fshost),
getModifiedTime: fshost.getModifiedTime?.bind(fshost),
getDirectories: fshost.getDirectories.bind(fshost),
getCurrentDirectory: fshost.getCurrentDirectory.bind(fshost),
getExecutingFilePath: fshost.getExecutingFilePath.bind(fshost),
realpath: fshost.realpath?.bind(fshost),
resolvePath: fshost.resolvePath.bind(fshost),
createDirectory: fshost.createDirectory.bind(fshost),
setModifiedTime: fshost.setModifiedTime?.bind(fshost),
deleteFile: fshost.deleteFile?.bind(fshost),
readDirectory: fshost.readDirectory.bind(fshost),
watchFile: fshost.watchFile.bind(fshost),
watchDirectory: fshost.watchDirectory.bind(fshost),
useCaseSensitiveFileNames: fshost.useCaseSensitiveFileNames,
// ServerHost
setTimeout: host.setTimeout.bind(host),
clearTimeout: host.clearTimeout.bind(host),
setImmediate: host.setImmediate.bind(host),
clearImmediate: host.clearImmediate.bind(host),
gc: host.gc?.bind(host),
trace: host.trace?.bind(host),
require: host.require?.bind(host),
// Sys
args: host.args,
newLine: host.newLine,
write: host.write.bind(host),
writeOutputIsTTY: host.writeOutputIsTTY?.bind(host),
getWidthOfTerminal: host.getWidthOfTerminal?.bind(host),
createHash: host.createHash?.bind(host),
createSHA256Hash: host.createSHA256Hash?.bind(host),
getMemoryUsage: host.getMemoryUsage?.bind(host),
exit: host.exit.bind(host),
enableCPUProfiler: host.enableCPUProfiler?.bind(host),
disableCPUProfiler: host.disableCPUProfiler?.bind(host),
cpuProfilingEnabled: host.cpuProfilingEnabled?.bind(host),
getEnvironmentVariable: host.getEnvironmentVariable.bind(host),
tryEnableSourceMapsForHost: host.tryEnableSourceMapsForHost?.bind(host),
debugMode: host.debugMode,
clearScreen: host.clearScreen?.bind(host),
setBlocking: host.setBlocking?.bind(host),
base64decode: host.base64decode?.bind(host),
base64encode: host.base64encode?.bind(host),
bufferFrom: host.bufferFrom?.bind(host),
defaultWatchFileKind: host.defaultWatchFileKind?.bind(host),
now: host.now?.bind(host),
disableUseFileVersionAsSignature: host.disableUseFileVersionAsSignature,
storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit,
// VirtualFS
ensureFileOrFolder: fshost.ensureFileOrFolder.bind(fshost),
} as any;
}
const noopConfigFileWatcher: FileWatcher = { close: noop };
/*@internal*/
@@ -761,7 +822,6 @@ namespace ts.server {
readonly toCanonicalFileName: (f: string) => string;
public readonly host: ServerHost;
public readonly fshost: FileServerHost;
public readonly logger: Logger;
public readonly cancellationToken: HostCancellationToken;
public readonly useSingleInferredProject: boolean;
@@ -807,8 +867,7 @@ namespace ts.server {
private performanceEventHandler?: PerformanceEventHandler;
constructor(opts: ProjectServiceOptions) {
this.host = opts.host;
this.fshost = opts.fshost || this.host;
this.host = getFileSystemHost(opts.host, opts.fshost);
this.logger = opts.logger;
this.cancellationToken = opts.cancellationToken;
this.useSingleInferredProject = opts.useSingleInferredProject;
@@ -836,11 +895,11 @@ namespace ts.server {
this.syntaxOnly = false;
}
if (this.fshost.realpath) {
if (this.host.realpath) {
this.realpathToScriptInfos = createMultiMap();
}
this.currentDirectory = toNormalizedPath(this.fshost.getCurrentDirectory());
this.toCanonicalFileName = createGetCanonicalFileName(this.fshost.useCaseSensitiveFileNames);
this.currentDirectory = toNormalizedPath(this.host.getCurrentDirectory());
this.toCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
this.globalCacheLocationDirectoryPath = this.typingsInstaller.globalTypingsCacheLocation
? ensureTrailingDirectorySeparator(this.toPath(this.typingsInstaller.globalTypingsCacheLocation))
: undefined;
@@ -864,7 +923,7 @@ namespace ts.server {
extraFileExtensions: [],
};
this.documentRegistry = createDocumentRegistryInternal(this.fshost.useCaseSensitiveFileNames, this.currentDirectory, this);
this.documentRegistry = createDocumentRegistryInternal(this.host.useCaseSensitiveFileNames, this.currentDirectory, this);
const watchLogLevel = this.logger.hasLevel(LogLevel.verbose) ? WatchLogLevel.Verbose :
this.logger.loggingEnabled() ? WatchLogLevel.TriggerOnly : WatchLogLevel.None;
const log: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => this.logger.info(s)) : noop;
@@ -874,7 +933,7 @@ namespace ts.server {
watchFile: returnNoopFileWatcher,
watchDirectory: returnNoopFileWatcher,
} :
getWatchFactory(this.fshost, watchLogLevel, log, getDetailWatchInfo);
getWatchFactory(this.host, watchLogLevel, log, getDetailWatchInfo);
}
toPath(fileName: string) {
@@ -883,12 +942,12 @@ namespace ts.server {
/*@internal*/
getExecutingFilePath() {
return this.getNormalizedAbsolutePath(this.fshost.getExecutingFilePath());
return this.getNormalizedAbsolutePath(this.host.getExecutingFilePath());
}
/*@internal*/
getNormalizedAbsolutePath(fileName: string) {
return getNormalizedAbsolutePath(fileName, this.fshost.getCurrentDirectory());
return getNormalizedAbsolutePath(fileName, this.host.getCurrentDirectory());
}
/*@internal*/
@@ -927,7 +986,7 @@ namespace ts.server {
private loadTypesMap() {
try {
const fileContent = this.fshost.readFile(this.typesMapLocation!); // TODO: GH#18217
const fileContent = this.host.readFile(this.typesMapLocation!); // TODO: GH#18217
if (fileContent === undefined) {
this.logger.info(`Provided types map file "${this.typesMapLocation}" doesn't exist`);
return;
@@ -1309,7 +1368,7 @@ namespace ts.server {
const fileOrDirectoryPath = this.toPath(fileOrDirectory);
const fsResult = config.cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
if (getBaseFileName(fileOrDirectoryPath) === "package.json" && !isInsideNodeModules(fileOrDirectoryPath) &&
(fsResult && fsResult.fileExists || !fsResult && this.fshost.fileExists(fileOrDirectoryPath))
(fsResult && fsResult.fileExists || !fsResult && this.host.fileExists(fileOrDirectoryPath))
) {
this.logger.info(`Config: ${configFileName} Detected new package.json: ${fileOrDirectory}`);
this.onAddPackageJson(fileOrDirectoryPath);
@@ -1325,7 +1384,7 @@ namespace ts.server {
currentDirectory: this.currentDirectory,
options: config.parsedCommandLine!.options,
program: configuredProjectForConfig?.getCurrentProgram() || config.parsedCommandLine!.fileNames,
useCaseSensitiveFileNames: this.fshost.useCaseSensitiveFileNames,
useCaseSensitiveFileNames: this.host.useCaseSensitiveFileNames,
writeLog: s => this.logger.info(s),
toPath: s => this.toPath(s)
})) return;
@@ -1554,7 +1613,7 @@ namespace ts.server {
// Closing file should trigger re-reading the file content from disk. This is
// because the user may chose to discard the buffer content before saving
// to the disk, and the server's version of the file can be out of sync.
const fileExists = info.isDynamic ? false : this.fshost.fileExists(info.fileName);
const fileExists = info.isDynamic ? false : this.host.fileExists(info.fileName);
info.close(fileExists);
this.stopWatchingConfigFilesForClosedScriptInfo(info);
@@ -1656,7 +1715,7 @@ namespace ts.server {
// Or the whole chain of config files for the roots of the inferred projects
// Cache the host value of file exists and add the info to map of open files impacted by this config file
const exists = this.fshost.fileExists(configFileName);
const exists = this.host.fileExists(configFileName);
let openFilesImpactedByConfigFile: ESMap<Path, boolean> | undefined;
if (isOpenScriptInfo(info)) {
(openFilesImpactedByConfigFile ||= new Map()).set(info.path, false);
@@ -1786,7 +1845,7 @@ namespace ts.server {
let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
if (!configFileExistenceInfo) {
// Create the cache
configFileExistenceInfo = { exists: this.fshost.fileExists(configFileName) };
configFileExistenceInfo = { exists: this.host.fileExists(configFileName) };
this.configFileExistenceInfoCache.set(canonicalConfigFilePath, configFileExistenceInfo);
}
@@ -1843,7 +1902,7 @@ namespace ts.server {
if (scriptInfo.isDynamic) return undefined;
let searchPath = asNormalizedPath(getDirectoryPath(info.fileName));
const isSearchPathInProjectRoot = () => containsPath(projectRootPath!, searchPath, this.currentDirectory, !this.fshost.useCaseSensitiveFileNames);
const isSearchPathInProjectRoot = () => containsPath(projectRootPath!, searchPath, this.currentDirectory, !this.host.useCaseSensitiveFileNames);
// If projectRootPath doesn't contain info.path, then do normal search for config file
const anySearchPathOk = !projectRootPath || !isSearchPathInProjectRoot();
@@ -1958,7 +2017,7 @@ namespace ts.server {
/** Get a filename if the language service exceeds the maximum allowed program size; otherwise returns undefined. */
private getFilenameForExceededTotalSizeLimitForNonTsFiles<T>(name: string, options: CompilerOptions | undefined, fileNames: T[], propertyReader: FilePropertyReader<T>): string | undefined {
if (options && options.disableSizeLimit || !this.fshost.getFileSize) {
if (options && options.disableSizeLimit || !this.host.getFileSize) {
return;
}
@@ -1974,12 +2033,12 @@ namespace ts.server {
continue;
}
totalNonTsFileSize += this.fshost.getFileSize(fileName);
totalNonTsFileSize += this.host.getFileSize(fileName);
if (totalNonTsFileSize > maxProgramSizeForNonTsFiles || totalNonTsFileSize > availableSpace) {
const top5LargestFiles = fileNames.map(f => propertyReader.getFileName(f))
.filter(name => !hasTSFileExtension(name))
.map(name => ({ name, size: this.fshost.getFileSize!(name) }))
.map(name => ({ name, size: this.host.getFileSize!(name) }))
.sort((a, b) => b.size - a.size)
.slice(0, 5);
this.logger.info(`Non TS file size exceeded limit (${totalNonTsFileSize}). Largest files: ${top5LargestFiles.map(file => `${file.name}:${file.size}`).join(", ")}`);
@@ -2082,7 +2141,7 @@ namespace ts.server {
}
if (!configFileExistenceInfo.config) {
configFileExistenceInfo.config = {
cachedDirectoryStructureHost: createCachedDirectoryStructureHost(this.fshost, this.fshost.getCurrentDirectory(), this.fshost.useCaseSensitiveFileNames)!,
cachedDirectoryStructureHost: createCachedDirectoryStructureHost(this.host, this.host.getCurrentDirectory(), this.host.useCaseSensitiveFileNames)!,
projects: new Map(),
reloadLevel: ConfigFileProgramReloadLevel.Full
};
@@ -2183,10 +2242,10 @@ namespace ts.server {
// Parse the config file and ensure its cached
const cachedDirectoryStructureHost = configFileExistenceInfo.config?.cachedDirectoryStructureHost ||
createCachedDirectoryStructureHost(this.fshost, this.fshost.getCurrentDirectory(), this.fshost.useCaseSensitiveFileNames)!;
createCachedDirectoryStructureHost(this.host, this.host.getCurrentDirectory(), this.host.useCaseSensitiveFileNames)!;
// Read updated contents from disk
const configFileContent = tryReadFile(configFilename, fileName => this.fshost.readFile(fileName));
const configFileContent = tryReadFile(configFilename, fileName => this.host.readFile(fileName));
const configFile = parseJsonText(configFilename, isString(configFileContent) ? configFileContent : "") as TsConfigSourceFile;
const configFileErrors = configFile.parseDiagnostics as Diagnostic[];
if (!isString(configFileContent)) configFileErrors.push(configFileContent);
@@ -2485,7 +2544,7 @@ namespace ts.server {
// ignore single inferred projects (handled elsewhere)
if (!project.projectRootPath) continue;
// ignore inferred projects that don't contain the root's path
if (!containsPath(project.projectRootPath, info.path, this.fshost.getCurrentDirectory(), !this.fshost.useCaseSensitiveFileNames)) continue;
if (!containsPath(project.projectRootPath, info.path, this.host.getCurrentDirectory(), !this.host.useCaseSensitiveFileNames)) continue;
// ignore inferred projects that are higher up in the project root.
// TODO(rbuckton): Should we add the file as a root to these as well?
if (bestMatch && bestMatch.projectRootPath!.length > project.projectRootPath.length) continue;
@@ -2630,7 +2689,7 @@ namespace ts.server {
(!this.globalCacheLocationDirectoryPath ||
!startsWith(info.path, this.globalCacheLocationDirectoryPath))) {
const indexOfNodeModules = info.path.indexOf("/node_modules/");
if (!this.fshost.getModifiedTime || indexOfNodeModules === -1) {
if (!this.host.getModifiedTime || indexOfNodeModules === -1) {
info.fileWatcher = this.watchFactory.watchFile(
info.fileName,
(_fileName, eventKind) => this.onSourceFileChanged(info, eventKind),
@@ -2728,7 +2787,7 @@ namespace ts.server {
}
private getModifiedTime(info: ScriptInfo) {
return (this.fshost.getModifiedTime!(info.path) || missingFileModifiedTime).getTime();
return (this.host.getModifiedTime!(info.path) || missingFileModifiedTime).getTime();
}
private refreshScriptInfo(info: ScriptInfo) {
@@ -2791,10 +2850,10 @@ namespace ts.server {
Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nOpen script files with non rooted disk path opened with current directory context cannot have same canonical names`);
Debug.assert(!isDynamic || this.currentDirectory === currentDirectory || this.useInferredProjectPerProjectRoot, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nDynamic files must always be opened with service's current directory or service should support inferred project per projectRootPath.`);
// If the file is not opened by client and the file doesnot exist on the disk, return
if (!openedByClient && !isDynamic && !(hostToQueryFileExistsOn || this.fshost).fileExists(fileName)) {
if (!openedByClient && !isDynamic && !(hostToQueryFileExistsOn || this.host).fileExists(fileName)) {
return;
}
info = new ScriptInfo(this.host, this.fshost, fileName, scriptKind!, !!hasMixedContent, path, this.filenameToScriptInfoVersion.get(path)); // TODO: GH#18217
info = new ScriptInfo(this.host, fileName, scriptKind!, !!hasMixedContent, path, this.filenameToScriptInfoVersion.get(path)); // TODO: GH#18217
this.filenameToScriptInfo.set(info.path, info);
this.filenameToScriptInfoVersion.delete(info.path);
if (!openedByClient) {
@@ -2832,7 +2891,7 @@ namespace ts.server {
/*@internal*/
getDocumentPositionMapper(project: Project, generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined {
// Since declaration info and map file watches arent updating project's directory structure host (which can cache file structure) use host
const declarationInfo = this.getOrCreateScriptInfoNotOpenedByClient(generatedFileName, project.currentDirectory, this.fshost);
const declarationInfo = this.getOrCreateScriptInfoNotOpenedByClient(generatedFileName, project.currentDirectory, this.host);
if (!declarationInfo) {
if (sourceFileName) {
// Project contains source file and it generates the generated file name
@@ -2869,7 +2928,7 @@ namespace ts.server {
let mapFileNameFromDeclarationInfo: string | undefined;
let readMapFile: ReadMapFile | undefined = (mapFileName, mapFileNameFromDts) => {
const mapInfo = this.getOrCreateScriptInfoNotOpenedByClient(mapFileName, project.currentDirectory, this.fshost);
const mapInfo = this.getOrCreateScriptInfoNotOpenedByClient(mapFileName, project.currentDirectory, this.host);
if (!mapInfo) {
mapFileNameFromDeclarationInfo = mapFileNameFromDts;
return undefined;
@@ -2948,7 +3007,7 @@ namespace ts.server {
}
// Need to look for other files.
const info = this.getOrCreateScriptInfoNotOpenedByClient(fileName, (project || this).currentDirectory, project ? project.directoryStructureHost : this.fshost);
const info = this.getOrCreateScriptInfoNotOpenedByClient(fileName, (project || this).currentDirectory, project ? project.directoryStructureHost : this.host);
if (!info) return undefined;
// Attach as source
@@ -3069,7 +3128,7 @@ namespace ts.server {
if (this.openFiles.has(info.path)) return; // Skip open files
if (!info.fileWatcher) return; // not watched file
// Handle as if file is changed or deleted
this.onSourceFileChanged(info, this.fshost.fileExists(info.fileName) ? FileWatcherEventKind.Changed : FileWatcherEventKind.Deleted);
this.onSourceFileChanged(info, this.host.fileExists(info.fileName) ? FileWatcherEventKind.Changed : FileWatcherEventKind.Deleted);
});
// Cancel all project updates since we will be updating them now
this.pendingProjectUpdates.forEach((_project, projectName) => {
@@ -3242,7 +3301,7 @@ namespace ts.server {
const { fileName } = originalLocation;
const scriptInfo = this.getScriptInfo(fileName);
if (!scriptInfo && !this.fshost.fileExists(fileName)) return undefined;
if (!scriptInfo && !this.host.fileExists(fileName)) return undefined;
const originalFileInfo: OriginalFileInfo = { fileName: toNormalizedPath(fileName), path: this.toPath(fileName) };
const configFileName = this.getConfigFileNameForFile(originalFileInfo);
@@ -3314,7 +3373,7 @@ namespace ts.server {
/** @internal */
fileExists(fileName: NormalizedPath): boolean {
return !!this.getScriptInfoForNormalizedPath(fileName) || this.fshost.fileExists(fileName);
return !!this.getScriptInfoForNormalizedPath(fileName) || this.host.fileExists(fileName);
}
private findExternalProjectContainingOpenScriptInfo(info: ScriptInfo): ExternalProject | undefined {
@@ -3696,15 +3755,17 @@ namespace ts.server {
/* @internal */
updateFileSystem(updatedFiles: protocol.FileSystemRequestArgs[] | undefined, deletedFiles?: string[]) {
Debug.assert(this.fshost instanceof VirtualFS.VirtualServerHost);
const fshost = this.host as unknown as FileServerHost;
if (updatedFiles) {
Debug.assert(fshost.ensureFileOrFolder)
for (const { file, fileContent } of updatedFiles) {
this.fshost.ensureFileOrFolder({ path: file, content: fileContent });
fshost.ensureFileOrFolder({ path: file, content: fileContent });
}
}
if (deletedFiles) {
Debug.assert(fshost.deleteFile)
for (const file of deletedFiles) {
this.fshost.deleteFile(file, /*deleteEmptyParentFolders*/ true);
fshost.deleteFile(file, /*deleteEmptyParentFolders*/ true);
}
}
}
@@ -3970,7 +4031,7 @@ namespace ts.server {
for (const file of proj.rootFiles) {
const normalized = toNormalizedPath(file.fileName);
if (getBaseConfigFileName(normalized)) {
if (this.serverMode === LanguageServiceMode.Semantic && this.fshost.fileExists(normalized)) {
if (this.serverMode === LanguageServiceMode.Semantic && this.host.fileExists(normalized)) {
(tsConfigFiles || (tsConfigFiles = [])).push(normalized);
}
}
@@ -4125,7 +4186,7 @@ namespace ts.server {
case Ternary.True: return directory;
case Ternary.False: return undefined;
case Ternary.Maybe:
return this.fshost.fileExists(combinePaths(directory, "package.json"))
return this.host.fileExists(combinePaths(directory, "package.json"))
? directory
: undefined;
}
+1 -1
View File
@@ -42,7 +42,7 @@ namespace ts.server {
};
function addOrUpdate(fileName: Path) {
const packageJsonInfo = Debug.checkDefined(createPackageJsonInfo(fileName, host.fshost));
const packageJsonInfo = Debug.checkDefined(createPackageJsonInfo(fileName, host.host));
packageJsons.set(fileName, packageJsonInfo);
directoriesWithoutPackageJson.delete(getDirectoryPath(fileName));
}
+22 -24
View File
@@ -85,7 +85,6 @@ namespace ts.server {
languageService: LanguageService;
languageServiceHost: LanguageServiceHost;
serverHost: ServerHost;
fsHost: FileServerHost;
session?: Session<unknown>;
config: any;
}
@@ -234,8 +233,8 @@ namespace ts.server {
return hasOneOrMoreJsAndNoTsFiles(this);
}
public static resolveModule(moduleName: string, initialDir: string, host: ServerHost, fshost: FileServerHost, log: (message: string) => void, logErrors?: (message: string) => void): {} | undefined {
const resolvedPath = normalizeSlashes(fshost.resolvePath(combinePaths(initialDir, "node_modules")));
public static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void, logErrors?: (message: string) => void): {} | undefined {
const resolvedPath = normalizeSlashes(host.resolvePath(combinePaths(initialDir, "node_modules")));
log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`);
const result = host.require!(resolvedPath, moduleName); // TODO: GH#18217
if (result.error) {
@@ -323,7 +322,7 @@ namespace ts.server {
else if (host.trace) {
this.trace = s => host.trace!(s);
}
this.realpath = maybeBind(this.projectService.fshost, this.projectService.fshost.realpath);
this.realpath = maybeBind(this.projectService.host, this.projectService.host.realpath);
// Use the current directory as resolution root only if the project created using current directory string
this.resolutionCache = createResolutionCache(
@@ -455,7 +454,7 @@ namespace ts.server {
}
useCaseSensitiveFileNames() {
return this.projectService.fshost.useCaseSensitiveFileNames;
return this.projectService.host.useCaseSensitiveFileNames;
}
readDirectory(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[] {
@@ -463,11 +462,11 @@ namespace ts.server {
}
readFile(fileName: string): string | undefined {
return this.projectService.fshost.readFile(fileName);
return this.projectService.host.readFile(fileName);
}
writeFile(fileName: string, content: string): void {
return this.projectService.fshost.writeFile(fileName, content);
return this.projectService.host.writeFile(fileName, content);
}
fileExists(file: string): boolean {
@@ -1608,7 +1607,7 @@ namespace ts.server {
(errorLogs || (errorLogs = [])).push(message);
};
const resolvedModule = firstDefined(searchPaths, searchPath =>
Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, this.projectService.fshost, log, logError) as PluginModuleFactory | undefined);
Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log, logError) as PluginModuleFactory | undefined);
if (resolvedModule) {
const configurationOverride = pluginConfigOverrides && pluginConfigOverrides.get(pluginConfigEntry.name);
if (configurationOverride) {
@@ -1638,7 +1637,6 @@ namespace ts.server {
languageService: this.languageService,
languageServiceHost: this,
serverHost: this.projectService.host,
fsHost: this.projectService.fshost,
session: this.projectService.session
};
@@ -1729,23 +1727,23 @@ namespace ts.server {
return {
fileExists: this.program.fileExists,
directoryExists: this.program.directoryExists,
realpath: this.program.realpath || this.projectService.fshost.realpath?.bind(this.projectService.fshost),
realpath: this.program.realpath || this.projectService.host.realpath?.bind(this.projectService.host),
getCurrentDirectory: this.getCurrentDirectory.bind(this),
readFile: this.projectService.fshost.readFile.bind(this.projectService.fshost),
getDirectories: this.projectService.fshost.getDirectories.bind(this.projectService.fshost),
readFile: this.projectService.host.readFile.bind(this.projectService.host),
getDirectories: this.projectService.host.getDirectories.bind(this.projectService.host),
trace: this.projectService.host.trace?.bind(this.projectService.host),
useCaseSensitiveFileNames: this.program.useCaseSensitiveFileNames(),
};
}
return {
fileExists: this.projectService.fshost.fileExists.bind(this.projectService.fshost),
directoryExists: this.projectService.fshost.directoryExists.bind(this.projectService.fshost),
realpath: this.projectService.fshost.realpath?.bind(this.projectService.fshost),
getCurrentDirectory: this.projectService.fshost.getCurrentDirectory.bind(this.projectService.fshost),
readFile: this.projectService.fshost.readFile.bind(this.projectService.fshost),
getDirectories: this.projectService.fshost.getDirectories.bind(this.projectService.fshost),
fileExists: this.projectService.host.fileExists.bind(this.projectService.host),
directoryExists: this.projectService.host.directoryExists.bind(this.projectService.host),
realpath: this.projectService.host.realpath?.bind(this.projectService.host),
getCurrentDirectory: this.projectService.host.getCurrentDirectory.bind(this.projectService.host),
readFile: this.projectService.host.readFile.bind(this.projectService.host),
getDirectories: this.projectService.host.getDirectories.bind(this.projectService.host),
trace: this.projectService.host.trace?.bind(this.projectService.host),
useCaseSensitiveFileNames: this.projectService.fshost.useCaseSensitiveFileNames,
useCaseSensitiveFileNames: this.projectService.host.useCaseSensitiveFileNames,
};
}
@@ -1931,7 +1929,7 @@ namespace ts.server {
compilerOptions,
/*compileOnSaveEnabled*/ false,
watchOptions,
projectService.fshost,
projectService.host,
currentDirectory);
this.typeAcquisition = typeAcquisition;
this.projectRootPath = projectRootPath && projectService.toCanonicalFileName(projectRootPath);
@@ -1998,7 +1996,7 @@ namespace ts.server {
compilerOptions,
/*compileOnSaveEnabled*/ false,
/*watchOptions*/ undefined,
projectService.fshost,
projectService.host,
/*currentDirectory*/ undefined);
}
@@ -2185,7 +2183,7 @@ namespace ts.server {
compilerOptions,
/*compileOnSaveEnabled*/ false,
hostProject.getWatchOptions(),
hostProject.projectService.fshost,
hostProject.projectService.host,
hostProject.currentDirectory);
this.rootFileNames = initialRootNames;
@@ -2363,7 +2361,7 @@ namespace ts.server {
// Ensure the config file existience info is cached
let configFileExistenceInfo = this.projectService.configFileExistenceInfoCache.get(canonicalConfigFilePath);
if (!configFileExistenceInfo) {
this.projectService.configFileExistenceInfoCache.set(canonicalConfigFilePath, configFileExistenceInfo = { exists: this.projectService.fshost.fileExists(configFileName) });
this.projectService.configFileExistenceInfoCache.set(canonicalConfigFilePath, configFileExistenceInfo = { exists: this.projectService.host.fileExists(configFileName) });
}
// Ensure we have upto date parsed command line
this.projectService.ensureParsedConfigUptoDate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, this);
@@ -2616,7 +2614,7 @@ namespace ts.server {
compilerOptions,
compileOnSaveEnabled,
watchOptions,
projectService.fshost,
projectService.host,
getDirectoryPath(projectFilePath || normalizeSlashes(externalProjectName)));
this.enableGlobalPlugins(this.getCompilerOptions(), pluginConfigOverrides);
}
+5 -6
View File
@@ -46,7 +46,7 @@ namespace ts.server {
*/
private pendingReloadFromDisk = false;
constructor(private readonly host: FileServerHost, private readonly info: ScriptInfo, initialVersion?: ScriptInfoVersion) {
constructor(private readonly host: ServerHost, private readonly info: ScriptInfo, initialVersion?: ScriptInfoVersion) {
this.version = initialVersion || { svc: 0, text: 0 };
}
@@ -330,7 +330,6 @@ namespace ts.server {
constructor(
private readonly host: ServerHost,
private readonly fshost: FileServerHost,
readonly fileName: NormalizedPath,
readonly scriptKind: ScriptKind,
public readonly hasMixedContent: boolean,
@@ -338,7 +337,7 @@ namespace ts.server {
initialVersion?: ScriptInfoVersion) {
this.isDynamic = isDynamicFileName(fileName);
this.textStorage = new TextStorage(fshost, this, initialVersion);
this.textStorage = new TextStorage(host, this, initialVersion);
if (hasMixedContent || this.isDynamic) {
this.textStorage.reload("");
this.realpath = this.path;
@@ -396,10 +395,10 @@ namespace ts.server {
if (this.realpath === undefined) {
// Default is just the path
this.realpath = this.path;
if (this.fshost.realpath) {
if (this.host.realpath) {
Debug.assert(!!this.containingProjects.length);
const project = this.containingProjects[0];
const realpath = this.fshost.realpath(this.path);
const realpath = this.host.realpath(this.path);
if (realpath) {
this.realpath = project.toPath(realpath);
// If it is different from this.path, add to the map
@@ -577,7 +576,7 @@ namespace ts.server {
}
saveTo(fileName: string) {
this.fshost.writeFile(fileName, getSnapshotText(this.textStorage.getSnapshot()));
this.host.writeFile(fileName, getSnapshotText(this.textStorage.getSnapshot()));
}
/*@internal*/
+3 -5
View File
@@ -782,11 +782,9 @@ namespace ts.server {
private suppressDiagnosticEvents?: boolean;
private eventHandler: ProjectServiceEventHandler | undefined;
private readonly noGetErrOnBackgroundUpdate?: boolean;
private fshost: FileServerHost;
constructor(opts: SessionOptions) {
this.host = opts.host;
this.fshost = opts.fshost || this.host;
this.host = getFileSystemHost(opts.host, opts.fshost);
this.cancellationToken = opts.cancellationToken;
this.typingsInstaller = opts.typingsInstaller;
this.byteLength = opts.byteLength;
@@ -2205,7 +2203,7 @@ namespace ts.server {
return args.richResponse ? { emitSkipped: true, diagnostics: [] } : false;
}
const scriptInfo = project.getScriptInfo(file)!;
const { emitSkipped, diagnostics } = project.emitFile(scriptInfo, (path, data, writeByteOrderMark) => this.fshost.writeFile(path, data, writeByteOrderMark));
const { emitSkipped, diagnostics } = project.emitFile(scriptInfo, (path, data, writeByteOrderMark) => this.host.writeFile(path, data, writeByteOrderMark));
return args.richResponse ?
{
emitSkipped,
@@ -2905,7 +2903,7 @@ namespace ts.server {
}
getCanonicalFileName(fileName: string) {
const name = this.fshost.useCaseSensitiveFileNames ? fileName : toFileNameLowerCase(fileName);
const name = this.host.useCaseSensitiveFileNames ? fileName : toFileNameLowerCase(fileName);
return normalizePath(name);
}
+10 -2
View File
@@ -17,10 +17,18 @@ declare namespace ts.server {
trace?(s: string): void;
require?(initialPath: string, moduleName: string): RequireResult;
}
export type FileServerHost = Pick<
ServerHost,
| "readFile" | "writeFile" | "fileExists" | "directoryExists" | "getFileSize" | "getModifiedTime"
| "getDirectories" | "getCurrentDirectory" | "getExecutingFilePath" | "realpath" | "resolvePath"
| "createDirectory" | "setModifiedTime" | "deleteFile" | "readDirectory" | "watchFile" | "watchDirectory"
| "useCaseSensitiveFileNames">;
| "createDirectory" | "setModifiedTime" | "readDirectory" | "watchFile" | "watchDirectory"
| "useCaseSensitiveFileNames"> & {
deleteFile(path: string, deleteEmptyParentFolders?: boolean): void;
ensureFileOrFolder(
fileOrDirectoryOrSymLink: { path: string } & ({ content: string, fileSize?: number } | {} | { symLink: string }),
ignoreWatchInvokedWithTriggerAsFileCreate?: boolean,
ignoreParentWatch?: boolean
): void
};
}
@@ -66,7 +66,7 @@ namespace ts.projectSystem {
const proxy = Harness.LanguageService.makeDefaultProxy(info);
proxy.getSemanticDiagnostics = filename => {
const prev = info.languageService.getSemanticDiagnostics(filename);
const sourceFile: SourceFile = info.project.getSourceFile(toPath(filename, /*basePath*/ undefined, createGetCanonicalFileName(info.fsHost.useCaseSensitiveFileNames)))!;
const sourceFile: SourceFile = info.project.getSourceFile(toPath(filename, /*basePath*/ undefined, createGetCanonicalFileName(info.serverHost.useCaseSensitiveFileNames)))!;
prev.push({
category: DiagnosticCategory.Warning,
file: sourceFile,
@@ -1,7 +1,7 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: webServer", () => {
class TestWorkerSession extends server.WorkerSession {
constructor(host: server.ServerHost, fshost: server.FileServerHost, webHost: server.HostWithWriteMessage, options: Partial<server.StartSessionOptions>, logger: server.Logger) {
constructor(host: server.ServerHost, fshost: server.FileServerHost | undefined, webHost: server.HostWithWriteMessage, options: Partial<server.StartSessionOptions>, logger: server.Logger) {
super(
host,
fshost,
@@ -37,12 +37,12 @@ namespace ts.projectSystem {
writeMessage: s => messages.push(s),
};
const webSys = server.createWebSystem(webHost, emptyArray, () => host.getExecutingFilePath());
let fshost = webSys;
let serverMode = LanguageServiceMode.PartialSemantic;
let fshost: VirtualFS.VirtualServerHost | undefined;
if (isVfs) {
serverMode = LanguageServiceMode.Semantic;
fshost = VirtualFS.createVirtualServerHost({ executingFilePath: "/a/lib/tsc.js" });
(fshost as VirtualFS.VirtualServerHost).ensureFileOrFolder(libFile);
serverMode = LanguageServiceMode.Semantic;
}
const logger = logLevel !== undefined ? new server.MainProcessLogger(logLevel, webHost) : nullLogger();
const session = new TestWorkerSession(webSys, fshost, webHost, { serverMode }, logger);
+6 -12
View File
@@ -28,7 +28,7 @@ namespace ts.server {
cancellationToken: ServerCancellationToken;
serverMode: LanguageServiceMode | undefined;
unknownServerMode?: string;
startSession: (option: StartSessionOptions, logger: Logger, cancellationToken: ServerCancellationToken, fs: ServerHost) => void;
startSession: (option: StartSessionOptions, logger: Logger, cancellationToken: ServerCancellationToken, fs: FileServerHost | undefined) => void;
}
function start({ args, logger, cancellationToken, serverMode, unknownServerMode, startSession: startServer }: StartInput, platform: string) {
const syntaxOnly = hasArgument("--syntaxOnly");
@@ -58,17 +58,11 @@ namespace ts.server {
console.warn = (...args) => logger.msg(args.length === 1 ? args[0] : args.join(", "), Msg.Err);
console.error = (...args) => logger.msg(args.length === 1 ? args[0] : args.join(", "), Msg.Err);
let fshost;
if (vfs) {
fshost = new VirtualFS.VirtualServerHost({
useCaseSensitiveFileNames: sys.useCaseSensitiveFileNames,
executingFilePath: directorySeparator, // Use same executingFilePath as webserver
newLine: sys.newLine,
});
}
else {
fshost = sys as ServerHost;
}
const fshost = vfs ? new VirtualFS.VirtualServerHost({
useCaseSensitiveFileNames: sys.useCaseSensitiveFileNames,
executingFilePath: directorySeparator, // Use same executingFilePath as webserver
newLine: sys.newLine,
}) : undefined;
startServer(
{
globalPlugins: findArgumentStringArray("--globalPlugins"),
+1 -1
View File
@@ -97,7 +97,7 @@ namespace ts.server {
return [seconds, nanoseconds];
}
function startWebSession(options: StartSessionOptions, logger: Logger, cancellationToken: ServerCancellationToken, fshost: ServerHost) {
function startWebSession(options: StartSessionOptions, logger: Logger, cancellationToken: ServerCancellationToken, fshost: FileServerHost | undefined) {
class WorkerSession extends server.WorkerSession {
constructor() {
super(sys as ServerHost, fshost, { writeMessage }, options, logger, cancellationToken, hrtime);
+1 -1
View File
@@ -178,7 +178,7 @@ namespace ts.server {
serverMode: SessionOptions["serverMode"];
}
export class WorkerSession extends Session<{}> {
constructor(host: ServerHost, fshost: FileServerHost, private webHost: HostWithWriteMessage, options: StartSessionOptions, logger: Logger, cancellationToken: ServerCancellationToken, hrtime: SessionOptions["hrtime"]) {
constructor(host: ServerHost, fshost: FileServerHost | undefined, private webHost: HostWithWriteMessage, options: StartSessionOptions, logger: Logger, cancellationToken: ServerCancellationToken, hrtime: SessionOptions["hrtime"]) {
super({
host,
fshost,
+12 -7
View File
@@ -6985,7 +6985,16 @@ declare namespace ts.server {
trace?(s: string): void;
require?(initialPath: string, moduleName: string): RequireResult;
}
type FileServerHost = Pick<ServerHost, "readFile" | "writeFile" | "fileExists" | "directoryExists" | "getFileSize" | "getModifiedTime" | "getDirectories" | "getCurrentDirectory" | "getExecutingFilePath" | "realpath" | "resolvePath" | "createDirectory" | "setModifiedTime" | "deleteFile" | "readDirectory" | "watchFile" | "watchDirectory" | "useCaseSensitiveFileNames">;
type FileServerHost = Pick<ServerHost, "readFile" | "writeFile" | "fileExists" | "directoryExists" | "getFileSize" | "getModifiedTime" | "getDirectories" | "getCurrentDirectory" | "getExecutingFilePath" | "realpath" | "resolvePath" | "createDirectory" | "setModifiedTime" | "deleteFile" | "readDirectory" | "watchFile" | "watchDirectory" | "useCaseSensitiveFileNames"> & {
ensureFileOrFolder(fileOrDirectoryOrSymLink: {
path: string;
} & ({
content: string;
fileSize?: number;
} | {} | {
symLink: string;
}), ignoreWatchInvokedWithTriggerAsFileCreate?: boolean, ignoreParentWatch?: boolean): void;
};
}
declare namespace ts.server {
enum LogLevel {
@@ -9917,7 +9926,6 @@ declare namespace ts.server {
function isDynamicFileName(fileName: NormalizedPath): boolean;
class ScriptInfo {
private readonly host;
private readonly fshost;
readonly fileName: NormalizedPath;
readonly scriptKind: ScriptKind;
readonly hasMixedContent: boolean;
@@ -9929,7 +9937,7 @@ declare namespace ts.server {
private formatSettings;
private preferences;
private textStorage;
constructor(host: ServerHost, fshost: FileServerHost, fileName: NormalizedPath, scriptKind: ScriptKind, hasMixedContent: boolean, path: Path, initialVersion?: ScriptInfoVersion);
constructor(host: ServerHost, fileName: NormalizedPath, scriptKind: ScriptKind, hasMixedContent: boolean, path: Path, initialVersion?: ScriptInfoVersion);
isScriptOpen(): boolean;
open(newText: string): void;
close(fileExists?: boolean): void;
@@ -9993,7 +10001,6 @@ declare namespace ts.server {
languageService: LanguageService;
languageServiceHost: LanguageServiceHost;
serverHost: ServerHost;
fsHost: FileServerHost;
session?: Session<unknown>;
config: any;
}
@@ -10058,7 +10065,7 @@ declare namespace ts.server {
private readonly cancellationToken;
isNonTsProject(): boolean;
isJsOnlyProject(): boolean;
static resolveModule(moduleName: string, initialDir: string, host: ServerHost, fshost: FileServerHost, log: (message: string) => void, logErrors?: (message: string) => void): {} | undefined;
static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void, logErrors?: (message: string) => void): {} | undefined;
isKnownTypesPackageName(name: string): boolean;
installPackage(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
private get typingsCache();
@@ -10444,7 +10451,6 @@ declare namespace ts.server {
readonly currentDirectory: NormalizedPath;
readonly toCanonicalFileName: (f: string) => string;
readonly host: ServerHost;
readonly fshost: FileServerHost;
readonly logger: Logger;
readonly cancellationToken: HostCancellationToken;
readonly useSingleInferredProject: boolean;
@@ -10689,7 +10695,6 @@ declare namespace ts.server {
private suppressDiagnosticEvents?;
private eventHandler;
private readonly noGetErrOnBackgroundUpdate?;
private fshost;
constructor(opts: SessionOptions);
private sendRequestCompletedEvent;
private addPerformanceData;