diff --git a/src/compiler/core.ts b/src/compiler/core.ts index b6472987398..1c180d5bc89 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -3000,8 +3000,4 @@ namespace ts { } export function assertTypeIsNever(_: never): void { } // tslint:disable-line no-empty - - export function getBoundFunction(method: T | undefined, methodOf: {}): T | undefined { - return method && method.bind(methodOf); - } } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 43211313237..ed5284e696c 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -144,17 +144,16 @@ namespace ts { enableStatistics(compilerOptions); const program = createProgram(rootFileNames, compilerOptions, compilerHost); - const exitStatus = compileProgram(program); - + const exitStatus = emitFilesAndReportErrors(program, reportDiagnostic, s => sys.write(s + sys.newLine)); reportStatistics(program); return sys.exit(exitStatus); } function updateWatchCompilationHost(watchCompilerHost: WatchCompilerHost) { - const compilerWithBuilderState = watchCompilerHost.afterProgramCreate; + const compileUsingBuilder = watchCompilerHost.afterProgramCreate; watchCompilerHost.beforeProgramCreate = enableStatistics; - watchCompilerHost.afterProgramCreate = (host, program) => { - compilerWithBuilderState(host, program); + watchCompilerHost.afterProgramCreate = program => { + compileUsingBuilder(program); reportStatistics(program); }; } @@ -175,40 +174,6 @@ namespace ts { createWatch(watchCompilerHost); } - function compileProgram(program: Program): ExitStatus { - let diagnostics: Diagnostic[]; - - // First get and report any syntactic errors. - diagnostics = program.getSyntacticDiagnostics().slice(); - - // If we didn't have any syntactic errors, then also try getting the global and - // semantic errors. - if (diagnostics.length === 0) { - diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics()); - - if (diagnostics.length === 0) { - diagnostics = program.getSemanticDiagnostics().slice(); - } - } - - // Emit and report any errors we ran into. - const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(); - addRange(diagnostics, emitDiagnostics); - - sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic); - writeFileAndEmittedFileList(sys, program, emittedFiles); - if (emitSkipped && diagnostics.length > 0) { - // If the emitter didn't emit anything, then pass that value along. - return ExitStatus.DiagnosticsPresent_OutputsSkipped; - } - else if (diagnostics.length > 0) { - // The emitter emitted something, inform the caller if that happened in the presence - // of diagnostics or not. - return ExitStatus.DiagnosticsPresent_OutputsGenerated; - } - return ExitStatus.Success; - } - function enableStatistics(compilerOptions: CompilerOptions) { if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) { performance.enable(); diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 67caf3c79ca..fcee17e0328 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -78,30 +78,152 @@ namespace ts { return configParseResult; } + /** + * Program structure needed to emit the files and report diagnostics + */ + export interface ProgramToEmitFilesAndReportErrors { + getCurrentDirectory(): string; + getCompilerOptions(): CompilerOptions; + getSourceFiles(): ReadonlyArray; + getSyntacticDiagnostics(): ReadonlyArray; + getOptionsDiagnostics(): ReadonlyArray; + getGlobalDiagnostics(): ReadonlyArray; + getSemanticDiagnostics(): ReadonlyArray; + emit(): EmitResult; + } + + /** + * Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options + */ + export function emitFilesAndReportErrors(program: ProgramToEmitFilesAndReportErrors, reportDiagnostic: DiagnosticReporter, writeFileName?: (s: string) => void) { + // First get and report any syntactic errors. + const diagnostics = program.getSyntacticDiagnostics().slice(); + let reportSemanticDiagnostics = false; + + // If we didn't have any syntactic errors, then also try getting the global and + // semantic errors. + if (diagnostics.length === 0) { + addRange(diagnostics, program.getOptionsDiagnostics()); + addRange(diagnostics, program.getGlobalDiagnostics()); + + if (diagnostics.length === 0) { + reportSemanticDiagnostics = true; + } + } + + // Emit and report any errors we ran into. + const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(); + addRange(diagnostics, emitDiagnostics); + + if (reportSemanticDiagnostics) { + addRange(diagnostics, program.getSemanticDiagnostics()); + } + + sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic); + if (writeFileName) { + const currentDir = program.getCurrentDirectory(); + forEach(emittedFiles, file => { + const filepath = getNormalizedAbsolutePath(file, currentDir); + writeFileName(`TSFILE: ${filepath}`); + }); + + if (program.getCompilerOptions().listFiles) { + forEach(program.getSourceFiles(), file => { + writeFileName(file.fileName); + }); + } + } + + if (emitSkipped && diagnostics.length > 0) { + // If the emitter didn't emit anything, then pass that value along. + return ExitStatus.DiagnosticsPresent_OutputsSkipped; + } + else if (diagnostics.length > 0) { + // The emitter emitted something, inform the caller if that happened in the presence + // of diagnostics or not. + return ExitStatus.DiagnosticsPresent_OutputsGenerated; + } + return ExitStatus.Success; + } + + /** + * Creates the function that emits files and reports errors when called with program + */ + function createEmitFilesAndReportErrorsWithBuilderUsingSystem(system: System, reportDiagnostic: DiagnosticReporter) { + const emitErrorsAndReportErrorsWithBuilder = createEmitFilesAndReportErrorsWithBuilder({ + useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, + createHash: system.createHash && (s => system.createHash(s)), + writeFile, + reportDiagnostic, + writeFileName: s => system.write(s + system.newLine) + }); + let host: CachedDirectoryStructureHost | undefined; + return { + emitFilesAndReportError: (program: Program) => emitErrorsAndReportErrorsWithBuilder(program), + setHost: (cachedDirectoryStructureHost: CachedDirectoryStructureHost) => host = cachedDirectoryStructureHost + }; + + function getHost() { + return host || system; + } + + function ensureDirectoriesExist(directoryPath: string) { + if (directoryPath.length > getRootLength(directoryPath) && !getHost().directoryExists(directoryPath)) { + const parentDirectory = getDirectoryPath(directoryPath); + ensureDirectoriesExist(parentDirectory); + getHost().createDirectory(directoryPath); + } + } + + function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) { + try { + performance.mark("beforeIOWrite"); + ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); + + getHost().writeFile(fileName, text, writeByteOrderMark); + + performance.mark("afterIOWrite"); + performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); + } + catch (e) { + if (onError) { + onError(e.message); + } + } + } + } + + const noopFileWatcher: FileWatcher = { close: noop }; + /** * Creates the watch compiler host that can be extended with config file or root file names and options host */ - function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHost { - return { + function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter): WatchCompilerHost { + const { emitFilesAndReportError, setHost } = createEmitFilesAndReportErrorsWithBuilderUsingSystem(system, reportDiagnostic); + const host: WatchCompilerHost = { useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, getNewLine: () => system.newLine, - getCurrentDirectory: getBoundFunction(system.getCurrentDirectory, system), + getCurrentDirectory: () => system.getCurrentDirectory(), getDefaultLibLocation, getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), - 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), - watchFile: getBoundFunction(system.watchFile, system), - watchDirectory: getBoundFunction(system.watchDirectory, system), - setTimeout: getBoundFunction(system.setTimeout, system), - clearTimeout: getBoundFunction(system.clearTimeout, system), - trace: getBoundFunction(system.write, system), + fileExists: path => system.fileExists(path), + readFile: (path, encoding) => system.readFile(path, encoding), + directoryExists: path => system.directoryExists(path), + getDirectories: path => system.getDirectories(path), + readDirectory: (path, extensions, exclude, include, depth) => system.readDirectory(path, extensions, exclude, include, depth), + realpath: system.realpath && (path => system.realpath(path)), + watchFile: system.watchFile ? ((path, callback, pollingInterval) => system.watchFile(path, callback, pollingInterval)) : () => noopFileWatcher, + watchDirectory: system.watchDirectory ? ((path, callback, recursive) => system.watchDirectory(path, callback, recursive)) : () => noopFileWatcher, + setTimeout: system.setTimeout ? ((callback, ms, ...args: any[]) => system.setTimeout.call(system, callback, ms, ...args)) : noop, + clearTimeout: system.clearTimeout ? (timeoutId => system.clearTimeout(timeoutId)) : noop, + trace: s => system.write(s), onWatchStatusChange, - afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic) + createDirectory: path => system.createDirectory(path), + writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark), + onCachedDirectoryStructureHostCreate: host => setHost(host), + afterProgramCreate: emitFilesAndReportError, }; + return host; function getDefaultLibLocation() { return getDirectoryPath(normalizePath(system.getExecutingFilePath())); @@ -140,7 +262,7 @@ namespace ts { * Creates the watch compiler host from system for compiling root files and options in watch mode */ export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHostOfFilesAndCompilerOptions { - const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfFilesAndCompilerOptions; + const host = createWatchCompilerHost(system, reportDiagnostic || createDiagnosticReporter(system)) as WatchCompilerHostOfFilesAndCompilerOptions; host.rootFiles = rootFiles; host.options = options; return host; @@ -150,99 +272,75 @@ namespace ts { namespace ts { export type DiagnosticReporter = (diagnostic: Diagnostic) => void; - /** - * Writes emitted files, source files depending on options - */ - /*@internal*/ - export function writeFileAndEmittedFileList(system: System, program: Program, emittedFiles: string[]) { - const currentDir = system.getCurrentDirectory(); - forEach(emittedFiles, file => { - const filepath = getNormalizedAbsolutePath(file, currentDir); - system.write(`TSFILE: ${filepath}${system.newLine}`); - }); + interface BuilderProgram extends ProgramToEmitFilesAndReportErrors { + updateProgram(program: Program): void; + } - if (program.getCompilerOptions().listFiles) { - forEach(program.getSourceFiles(), file => { - system.write(file.fileName + system.newLine); - }); + function createBuilderProgram(host: BuilderEmitHost): BuilderProgram { + const builder = createEmitAndSemanticDiagnosticsBuilder({ + getCanonicalFileName: createGetCanonicalFileName(host.useCaseSensitiveFileNames()), + computeHash: host.createHash ? host.createHash : identity + }); + let program: Program; + return { + getCurrentDirectory: () => program.getCurrentDirectory(), + getCompilerOptions: () => program.getCompilerOptions(), + getSourceFiles: () => program.getSourceFiles(), + getSyntacticDiagnostics: () => program.getSyntacticDiagnostics(), + getOptionsDiagnostics: () => program.getOptionsDiagnostics(), + getGlobalDiagnostics: () => program.getGlobalDiagnostics(), + getSemanticDiagnostics: () => builder.getSemanticDiagnostics(program), + emit, + updateProgram + }; + + function updateProgram(p: Program) { + program = p; + builder.updateProgram(p); + } + + function emit(): EmitResult { + // Emit and report any errors we ran into. + let sourceMaps: SourceMapData[]; + let emitSkipped: boolean; + let diagnostics: Diagnostic[]; + let emittedFiles: string[]; + + let affectedEmitResult: AffectedFileResult; + while (affectedEmitResult = builder.emitNextAffectedFile(program, host.writeFile)) { + emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped; + diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics); + emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles); + sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps); + } + return { + emitSkipped, + diagnostics, + emittedFiles, + sourceMaps + }; } } /** - * Creates the function that compiles the program by maintaining the builder for the program and reports the errors and emits files + * Host needed to emit files and report errors using builder */ - export function createProgramCompilerWithBuilderState(system = sys, reportDiagnostic?: DiagnosticReporter) { - reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); - const builder = createEmitAndSemanticDiagnosticsBuilder({ - getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), - computeHash: getBoundFunction(system.createHash, system) || identity - }); + export interface BuilderEmitHost { + useCaseSensitiveFileNames(): boolean; + createHash?: (data: string) => string; + writeFile: WriteFileCallback; + reportDiagnostic: DiagnosticReporter; + writeFileName?: (s: string) => void; + } - return (host: DirectoryStructureHost, program: Program) => { - builder.updateProgram(program); - - // First get and report any syntactic errors. - const diagnostics = program.getSyntacticDiagnostics().slice(); - let reportSemanticDiagnostics = false; - - // If we didn't have any syntactic errors, then also try getting the global and - // semantic errors. - if (diagnostics.length === 0) { - addRange(diagnostics, program.getOptionsDiagnostics()); - addRange(diagnostics, program.getGlobalDiagnostics()); - - if (diagnostics.length === 0) { - reportSemanticDiagnostics = true; - } - } - - // Emit and report any errors we ran into. - const emittedFiles: string[] = program.getCompilerOptions().listEmittedFiles ? [] : undefined; - let sourceMaps: SourceMapData[]; - let emitSkipped: boolean; - - let affectedEmitResult: AffectedFileResult; - while (affectedEmitResult = builder.emitNextAffectedFile(program, writeFile)) { - emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped; - addRange(diagnostics, affectedEmitResult.result.diagnostics); - sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps); - } - - if (reportSemanticDiagnostics) { - addRange(diagnostics, builder.getSemanticDiagnostics(program)); - } - - sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic); - writeFileAndEmittedFileList(system, program, emittedFiles); - - function ensureDirectoriesExist(directoryPath: string) { - if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) { - const parentDirectory = getDirectoryPath(directoryPath); - ensureDirectoriesExist(parentDirectory); - host.createDirectory(directoryPath); - } - } - - function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) { - try { - performance.mark("beforeIOWrite"); - ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); - - host.writeFile(fileName, text, writeByteOrderMark); - - performance.mark("afterIOWrite"); - performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); - - if (emittedFiles) { - emittedFiles.push(fileName); - } - } - catch (e) { - if (onError) { - onError(e.message); - } - } - } + /** + * Creates the function that reports the program errors and emit files every time it is called with argument as program + */ + export function createEmitFilesAndReportErrorsWithBuilder(host: BuilderEmitHost) { + const builderProgram = createBuilderProgram(host); + return (program: Program) => { + builderProgram.updateProgram(program); + emitFilesAndReportErrors(builderProgram, host.reportDiagnostic, host.writeFileName); }; } @@ -250,7 +348,7 @@ namespace ts { /** If provided, callback to invoke before each program creation */ beforeProgramCreate?(compilerOptions: CompilerOptions): void; /** If provided, callback to invoke after every new program creation */ - afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; + afterProgramCreate?(program: Program): void; /** If provided, called with Diagnostic message that informs about change in watch status */ onWatchStatusChange?(diagnostic: Diagnostic, newLine: string): void; @@ -297,6 +395,14 @@ namespace ts { clearTimeout?(timeoutId: any): void; } + /** Internal interface used to wire emit through same host */ + /*@internal*/ + export interface WatchCompilerHost { + createDirectory?(path: string): void; + writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void; + onCachedDirectoryStructureHostCreate?(host: CachedDirectoryStructureHost): void; + } + /** * Host to create watch with root files and options */ @@ -315,12 +421,12 @@ namespace ts { /** * Reports the diagnostics in reading/writing or parsing of the config file */ - onConfigFileDiagnostic(diagnostic: Diagnostic): void; + onConfigFileDiagnostic: DiagnosticReporter; /** * Reports unrecoverable error when parsing config file */ - onUnRecoverableConfigFileDiagnostic(diagnostic: Diagnostic): void; + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; } /** @@ -345,7 +451,6 @@ namespace ts { */ /*@internal*/ export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { - cachedDirectoryStructureHost?: CachedDirectoryStructureHost; rootFiles?: string[]; options?: CompilerOptions; optionsToExtend?: CompilerOptions; @@ -418,23 +523,23 @@ namespace ts { const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames(); const currentDirectory = host.getCurrentDirectory(); const getCurrentDirectory = () => currentDirectory; - const onConfigFileDiagnostic = getBoundFunction(host.onConfigFileDiagnostic, host); - const readFile = getBoundFunction(host.readFile, host); + const readFile: (path: string, encoding?: string) => string | undefined = (path, encoding) => host.readFile(path, encoding); const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {} } = host; - const beforeProgramCreate = getBoundFunction(host.beforeProgramCreate, host) || noop; - const afterProgramCreate = getBoundFunction(host.afterProgramCreate, host) || noop; let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host; const cachedDirectoryStructureHost = configFileName && createCachedDirectoryStructureHost(host, currentDirectory, useCaseSensitiveFileNames); + if (cachedDirectoryStructureHost && host.onCachedDirectoryStructureHostCreate) { + host.onCachedDirectoryStructureHostCreate(cachedDirectoryStructureHost); + } const directoryStructureHost: DirectoryStructureHost = cachedDirectoryStructureHost || host; const parseConfigFileHost: ParseConfigFileHost = { useCaseSensitiveFileNames, - readDirectory: getBoundFunction(directoryStructureHost.readDirectory, directoryStructureHost), - fileExists: getBoundFunction(directoryStructureHost.fileExists, directoryStructureHost), + readDirectory: (path, extensions, exclude, include, depth) => directoryStructureHost.readDirectory(path, extensions, exclude, include, depth), + fileExists: path => host.fileExists(path), readFile, getCurrentDirectory, - onConfigFileDiagnostic, - onUnRecoverableConfigFileDiagnostic: getBoundFunction(host.onUnRecoverableConfigFileDiagnostic, host) + onConfigFileDiagnostic: host.onConfigFileDiagnostic, + onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic }; // From tsc we want to get already parsed result and hence check for rootFileNames @@ -460,8 +565,8 @@ namespace ts { // Members for CompilerHost getSourceFile: (fileName, languageVersion, onError?, shouldCreateNewSourceFile?) => getVersionedSourceFileByPath(fileName, toPath(fileName), languageVersion, onError, shouldCreateNewSourceFile), getSourceFileByPath: getVersionedSourceFileByPath, - getDefaultLibLocation: getBoundFunction(host.getDefaultLibLocation, host), - getDefaultLibFileName: getBoundFunction(host.getDefaultLibFileName, host), + getDefaultLibLocation: host.getDefaultLibLocation && (() => host.getDefaultLibLocation()), + getDefaultLibFileName: options => host.getDefaultLibFileName(options), writeFile: notImplemented, getCurrentDirectory, useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, @@ -470,9 +575,9 @@ namespace ts { fileExists, readFile, trace, - directoryExists: getBoundFunction(directoryStructureHost.directoryExists, directoryStructureHost), - getDirectories: getBoundFunction(directoryStructureHost.getDirectories, directoryStructureHost), - realpath: getBoundFunction(host.realpath, host), + directoryExists: directoryStructureHost.directoryExists && (path => directoryStructureHost.directoryExists(path)), + getDirectories: directoryStructureHost.getDirectories && (path => directoryStructureHost.getDirectories(path)), + realpath: host.realpath && (s => host.realpath(s)), onReleaseOldSourceFile, // Members for ResolutionCacheHost toPath, @@ -495,8 +600,8 @@ namespace ts { ); // Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names compilerHost.resolveModuleNames = host.resolveModuleNames ? - host.resolveModuleNames.bind(host) : - resolutionCache.resolveModuleNames.bind(resolutionCache); + ((moduleNames, containingFile, reusedNames) => host.resolveModuleNames(moduleNames, containingFile, reusedNames)) : + ((moduleNames, containingFile, reusedNames) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames)); compilerHost.resolveTypeReferenceDirectives = resolutionCache.resolveTypeReferenceDirectives.bind(resolutionCache); reportWatchDiagnostic(Diagnostics.Starting_compilation_in_watch_mode); @@ -524,7 +629,9 @@ namespace ts { return program; } - beforeProgramCreate(compilerOptions); + if (host.beforeProgramCreate) { + host.beforeProgramCreate(compilerOptions); + } // Compile the program const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !program; @@ -555,7 +662,9 @@ namespace ts { missingFilePathsRequestedForRelease = undefined; } - afterProgramCreate(directoryStructureHost, program); + if (host.afterProgramCreate) { + host.afterProgramCreate(program); + } reportWatchDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes); return program; } @@ -722,7 +831,7 @@ namespace ts { function reloadFileNamesFromConfigFile() { const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, parseConfigFileHost); if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) { - onConfigFileDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName)); + host.onConfigFileDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName)); } rootFileNames = result.fileNames; diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index bf5a42b7b08..24c9737fb26 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -49,12 +49,12 @@ namespace ts { return { useCaseSensitiveFileNames, fileExists, - readFile: getBoundFunction(host.readFile, host), + readFile: (path, encoding) => host.readFile(path, encoding), directoryExists: host.directoryExists && directoryExists, getDirectories, readDirectory, - createDirectory, - writeFile, + createDirectory: host.createDirectory && createDirectory, + writeFile: host.writeFile && writeFile, addOrDeleteFileOrDirectory, addOrDeleteFile, clearCache diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index bafe90a59cb..6afeae16ef5 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1765,11 +1765,11 @@ namespace ts.server { return this.getOrCreateScriptInfoWorker(fileName, currentDirectory, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent); } - getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost) { + getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: { fileExists(path: string): boolean; }) { return this.getOrCreateScriptInfoWorker(fileName, this.currentDirectory, openedByClient, fileContent, scriptKind, hasMixedContent, hostToQueryFileExistsOn); } - private getOrCreateScriptInfoWorker(fileName: NormalizedPath, currentDirectory: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost) { + private getOrCreateScriptInfoWorker(fileName: NormalizedPath, currentDirectory: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: { fileExists(path: string): boolean; }) { Debug.assert(fileContent === undefined || openedByClient, "ScriptInfo needs to be opened by client to be able to set its user defined content"); const path = normalizedPathToPath(fileName, currentDirectory, this.toCanonicalFileName); let info = this.getScriptInfoForPath(path); diff --git a/src/server/project.ts b/src/server/project.ts index 9ae1b1d0b6e..26d8aba0bd5 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -201,6 +201,9 @@ namespace ts.server { /*@internal*/ readonly currentDirectory: string; + /*@internal*/ + public directoryStructureHost: DirectoryStructureHost; + /*@internal*/ constructor( /*@internal*/readonly projectName: string, @@ -211,8 +214,9 @@ namespace ts.server { languageServiceEnabled: boolean, private compilerOptions: CompilerOptions, public compileOnSaveEnabled: boolean, - /*@internal*/public directoryStructureHost: DirectoryStructureHost, + directoryStructureHost: DirectoryStructureHost, currentDirectory: string | undefined) { + this.directoryStructureHost = directoryStructureHost; this.currentDirectory = this.projectService.getNormalizedAbsolutePath(currentDirectory || ""); this.cancellationToken = new ThrottledCancellationToken(this.projectService.cancellationToken, this.projectService.throttleWaitMilliseconds); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ca399847583..4abb45a11e4 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7270,7 +7270,6 @@ declare namespace ts.server { private documentRegistry; private compilerOptions; compileOnSaveEnabled: boolean; - directoryStructureHost: DirectoryStructureHost; private rootFiles; private rootFilesMap; private program; @@ -7741,7 +7740,9 @@ declare namespace ts.server { getScriptInfo(uncheckedFileName: string): ScriptInfo; private watchClosedScriptInfo(info); private stopWatchingScriptInfo(info); - getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost): ScriptInfo; + getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: { + fileExists(path: string): boolean; + }): ScriptInfo; private getOrCreateScriptInfoWorker(fileName, currentDirectory, openedByClient, fileContent?, scriptKind?, hasMixedContent?, hostToQueryFileExistsOn?); /** * This gets the script info for the normalized path. If the path is not rooted disk path then the open script info with project root context is preferred diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 521fd1e0ad6..eed0c7cc5d8 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3811,14 +3811,24 @@ declare namespace ts { declare namespace ts { type DiagnosticReporter = (diagnostic: Diagnostic) => void; /** - * Creates the function that compiles the program by maintaining the builder for the program and reports the errors and emits files + * Host needed to emit files and report errors using builder */ - function createProgramCompilerWithBuilderState(system?: System, reportDiagnostic?: DiagnosticReporter): (host: DirectoryStructureHost, program: Program) => void; + interface BuilderEmitHost { + useCaseSensitiveFileNames(): boolean; + createHash?: (data: string) => string; + writeFile: WriteFileCallback; + reportDiagnostic: DiagnosticReporter; + writeFileName?: (s: string) => void; + } + /** + * Creates the function that reports the program errors and emit files every time it is called with argument as program + */ + function createEmitFilesAndReportErrorsWithBuilder(host: BuilderEmitHost): (program: Program) => void; interface WatchCompilerHost { /** If provided, callback to invoke before each program creation */ beforeProgramCreate?(compilerOptions: CompilerOptions): void; /** If provided, callback to invoke after every new program creation */ - afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; + afterProgramCreate?(program: Program): void; /** If provided, called with Diagnostic message that informs about change in watch status */ onWatchStatusChange?(diagnostic: Diagnostic, newLine: string): void; useCaseSensitiveFileNames(): boolean; @@ -3852,7 +3862,7 @@ declare namespace ts { watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; - /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together*/ + /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */ setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; /** If provided, will be used to reset existing delayed compilation */ clearTimeout?(timeoutId: any): void; @@ -3873,11 +3883,11 @@ declare namespace ts { /** * Reports the diagnostics in reading/writing or parsing of the config file */ - onConfigFileDiagnostic(diagnostic: Diagnostic): void; + onConfigFileDiagnostic: DiagnosticReporter; /** * Reports unrecoverable error when parsing config file */ - onUnRecoverableConfigFileDiagnostic(diagnostic: Diagnostic): void; + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; } /** * Host to create watch with config file