diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 73fd485cb29..26506bf0436 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -9,7 +9,7 @@ import { EmitAndSemanticDiagnosticsBuilderProgram, EmitOnly, EmitResult, emitSkippedWithNoDiagnostics, emptyArray, ensurePathIsNonModuleName, filterSemanticDiagnostics, forEach, forEachEntry, forEachKey, generateDjb2Hash, GetCanonicalFileName, getDirectoryPath, getEmitDeclarations, getNormalizedAbsolutePath, getOptionsNameMap, - getOwnKeys, getRelativePathFromDirectory, getTsBuildInfoEmitOutputFilePath, handleNoEmitOptions, isArray, + getOwnKeys, getRelativePathFromDirectory, getTsBuildInfoEmitOutputFilePath, handleNoEmitOptions, HostForComputeHash, isArray, isDeclarationFileName, isJsonSourceFile, isNumber, isString, map, mapDefined, maybeBind, noop, notImplemented, outFile, Path, Program, ProjectReference, ReadBuildProgramHost, ReadonlyCollection, returnFalse, returnUndefined, SemanticDiagnosticsBuilderProgram, skipTypeChecking, some, SourceFile, @@ -220,8 +220,8 @@ function hasSameKeys(map1: ReadonlyCollection | undefined, map2: Readonl /** * Create the state so that we can iterate on changedFiles/affected files */ -function createBuilderProgramState(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState: Readonly | undefined, disableUseFileVersionAsSignature: boolean | undefined): BuilderProgramState { - const state = BuilderState.create(newProgram, getCanonicalFileName, oldState, disableUseFileVersionAsSignature) as BuilderProgramState; +function createBuilderProgramState(newProgram: Program, oldState: Readonly | undefined, disableUseFileVersionAsSignature: boolean | undefined): BuilderProgramState { + const state = BuilderState.create(newProgram, oldState, disableUseFileVersionAsSignature) as BuilderProgramState; state.program = newProgram; const compilerOptions = newProgram.getCompilerOptions(); state.compilerOptions = compilerOptions; @@ -297,7 +297,7 @@ function createBuilderProgramState(newProgram: Program, getCanonicalFileName: Ge // Unchanged file copy diagnostics const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath); if (diagnostics) { - state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], newProgram, getCanonicalFileName) : diagnostics as readonly Diagnostic[]); + state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], newProgram) : diagnostics as readonly Diagnostic[]); if (!state.semanticDiagnosticsFromOldState) { state.semanticDiagnosticsFromOldState = new Set(); } @@ -381,9 +381,10 @@ function getEmitSignatureFromOldSignature(options: CompilerOptions, oldOptions: isString(oldEmitSignature) ? [oldEmitSignature] : oldEmitSignature[0]; } -function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newProgram: Program, getCanonicalFileName: GetCanonicalFileName): readonly Diagnostic[] { +function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newProgram: Program): readonly Diagnostic[] { if (!diagnostics.length) return emptyArray; - const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(newProgram.getCompilerOptions())!, newProgram.getCurrentDirectory())); + let buildInfoDirectory: string | undefined; + let getCanonicalFileName: GetCanonicalFileName | undefined; return diagnostics.map(diagnostic => { const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPath); result.reportsUnnecessary = diagnostic.reportsUnnecessary; @@ -400,7 +401,8 @@ function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newPro }); function toPath(path: string) { - return ts.toPath(path, buildInfoDirectory, getCanonicalFileName); + buildInfoDirectory ??= getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(newProgram.getCompilerOptions())!, newProgram.getCurrentDirectory())); + return ts.toPath(path, buildInfoDirectory, getCanonicalFileName ??= createGetCanonicalFileName(newProgram.useCaseSensitiveFileNames())); } } @@ -463,8 +465,6 @@ function assertSourceFileOkWithoutNextAffectedCall(state: BuilderProgramState, s function getNextAffectedFile( state: BuilderProgramState, cancellationToken: CancellationToken | undefined, - computeHash: BuilderState.ComputeHash, - getCanonicalFileName: GetCanonicalFileName, host: BuilderProgramHost ): SourceFile | Program | undefined { while (true) { @@ -482,8 +482,6 @@ function getNextAffectedFile( state, affectedFile, cancellationToken, - computeHash, - getCanonicalFileName, host ); return affectedFile; @@ -522,8 +520,7 @@ function getNextAffectedFile( program, nextKey.value, cancellationToken, - computeHash, - getCanonicalFileName, + host, ); state.currentChangedFilePath = nextKey.value; state.affectedFilesIndex = 0; @@ -581,8 +578,6 @@ function handleDtsMayChangeOfAffectedFile( state: BuilderProgramState, affectedFile: SourceFile, cancellationToken: CancellationToken | undefined, - computeHash: BuilderState.ComputeHash, - getCanonicalFileName: GetCanonicalFileName, host: BuilderProgramHost, ) { removeSemanticDiagnosticsOf(state, affectedFile.resolvedPath); @@ -598,8 +593,7 @@ function handleDtsMayChangeOfAffectedFile( Debug.checkDefined(state.program), affectedFile, cancellationToken, - computeHash, - getCanonicalFileName, + host, ); return; } @@ -608,8 +602,6 @@ function handleDtsMayChangeOfAffectedFile( state, affectedFile, cancellationToken, - computeHash, - getCanonicalFileName, host, ); } @@ -622,8 +614,6 @@ function handleDtsMayChangeOf( state: BuilderProgramState, path: Path, cancellationToken: CancellationToken | undefined, - computeHash: BuilderState.ComputeHash, - getCanonicalFileName: GetCanonicalFileName, host: BuilderProgramHost ): void { removeSemanticDiagnosticsOf(state, path); @@ -642,8 +632,7 @@ function handleDtsMayChangeOf( program, sourceFile, cancellationToken, - computeHash, - getCanonicalFileName, + host, !host.disableUseFileVersionAsSignature ); // If not dts emit, nothing more to do @@ -677,8 +666,6 @@ function handleDtsMayChangeOfGlobalScope( state: BuilderProgramState, filePath: Path, cancellationToken: CancellationToken | undefined, - computeHash: BuilderState.ComputeHash, - getCanonicalFileName: GetCanonicalFileName, host: BuilderProgramHost, ): boolean { if (!state.fileInfos.get(filePath)?.affectsGlobalScope) return false; @@ -688,8 +675,6 @@ function handleDtsMayChangeOfGlobalScope( state, file.resolvedPath, cancellationToken, - computeHash, - getCanonicalFileName, host, )); removeDiagnosticsOfLibraryFiles(state); @@ -703,8 +688,6 @@ function handleDtsMayChangeOfReferencingExportOfAffectedFile( state: BuilderProgramState, affectedFile: SourceFile, cancellationToken: CancellationToken | undefined, - computeHash: BuilderState.ComputeHash, - getCanonicalFileName: GetCanonicalFileName, host: BuilderProgramHost ) { // If there was change in signature (dts output) for the changed file, @@ -722,8 +705,8 @@ function handleDtsMayChangeOfReferencingExportOfAffectedFile( const currentPath = queue.pop()!; if (!seenFileNamesMap.has(currentPath)) { seenFileNamesMap.set(currentPath, true); - if (handleDtsMayChangeOfGlobalScope(state, currentPath, cancellationToken, computeHash, getCanonicalFileName, host)) return; - handleDtsMayChangeOf(state, currentPath, cancellationToken, computeHash, getCanonicalFileName, host); + if (handleDtsMayChangeOfGlobalScope(state, currentPath, cancellationToken, host)) return; + handleDtsMayChangeOf(state, currentPath, cancellationToken, host); if (isChangedSignature(state, currentPath)) { const currentSourceFile = Debug.checkDefined(state.program).getSourceFileByPath(currentPath)!; queue.push(...BuilderState.getReferencedByPaths(state, currentSourceFile.resolvedPath)); @@ -736,7 +719,7 @@ function handleDtsMayChangeOfReferencingExportOfAffectedFile( // Go through exported modules from cache first // If exported modules has path, all files referencing file exported from are affected state.exportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath => { - if (handleDtsMayChangeOfGlobalScope(state, exportedFromPath, cancellationToken, computeHash, getCanonicalFileName, host)) return true; + if (handleDtsMayChangeOfGlobalScope(state, exportedFromPath, cancellationToken, host)) return true; const references = state.referencedMap!.getKeys(exportedFromPath); return references && forEachKey(references, filePath => handleDtsMayChangeOfFileAndExportsOfFile( @@ -744,8 +727,6 @@ function handleDtsMayChangeOfReferencingExportOfAffectedFile( filePath, seenFileAndExportsOfFile, cancellationToken, - computeHash, - getCanonicalFileName, host, ) ); @@ -761,14 +742,12 @@ function handleDtsMayChangeOfFileAndExportsOfFile( filePath: Path, seenFileAndExportsOfFile: Set, cancellationToken: CancellationToken | undefined, - computeHash: BuilderState.ComputeHash, - getCanonicalFileName: GetCanonicalFileName, host: BuilderProgramHost, ): boolean | undefined { if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) return undefined; - if (handleDtsMayChangeOfGlobalScope(state, filePath, cancellationToken, computeHash, getCanonicalFileName, host)) return true; - handleDtsMayChangeOf(state, filePath, cancellationToken, computeHash, getCanonicalFileName, host); + if (handleDtsMayChangeOfGlobalScope(state, filePath, cancellationToken, host)) return true; + handleDtsMayChangeOf(state, filePath, cancellationToken, host); // If exported modules has path, all files referencing file exported from are affected state.exportedModulesMap!.getKeys(filePath)?.forEach(exportedFromPath => @@ -777,8 +756,6 @@ function handleDtsMayChangeOfFileAndExportsOfFile( exportedFromPath, seenFileAndExportsOfFile, cancellationToken, - computeHash, - getCanonicalFileName, host, ) ); @@ -790,8 +767,6 @@ function handleDtsMayChangeOfFileAndExportsOfFile( state, referencingFilePath, cancellationToken, - computeHash, - getCanonicalFileName, host, ) ); @@ -919,8 +894,9 @@ export function isProgramBundleEmitBuildInfo(info: ProgramBuildInfo): info is Pr /** * Gets the program information to be emitted in buildInfo so that we can use it to create new program */ -function getBuildInfo(state: BuilderProgramState, getCanonicalFileName: GetCanonicalFileName, bundle: BundleBuildInfo | undefined): BuildInfo { +function getBuildInfo(state: BuilderProgramState, bundle: BundleBuildInfo | undefined): BuildInfo { const currentDirectory = Debug.checkDefined(state.program).getCurrentDirectory(); + const getCanonicalFileName = createGetCanonicalFileName(state.program!.useCaseSensitiveFileNames()); const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(state.compilerOptions)!, currentDirectory)); // Convert the file name to Path here if we set the fileName instead to optimize multiple d.ts file emits and having to compute Canonical path const latestChangedDtsFile = state.latestChangedDtsFile ? relativeToBuildInfoEnsuringAbsolutePath(state.latestChangedDtsFile) : undefined; @@ -1214,12 +1190,13 @@ function getTextHandlingSourceMapForSignature(text: string, data: WriteFileCallb /** @internal */ export function computeSignatureWithDiagnostics( + program: Program, sourceFile: SourceFile, text: string, - computeHash: BuilderState.ComputeHash | undefined, - getCanonicalFileName: GetCanonicalFileName, + host: HostForComputeHash, data: WriteFileCallbackData | undefined ) { + let getCanonicalFileName: GetCanonicalFileName | undefined; text = getTextHandlingSourceMapForSignature(text, data); let sourceFileDirectory: string | undefined; if (data?.diagnostics?.length) { @@ -1227,7 +1204,7 @@ export function computeSignatureWithDiagnostics( `${locationInfo(diagnostic)}${DiagnosticCategory[diagnostic.category]}${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText)}` ).join("\n"); } - return (computeHash ?? generateDjb2Hash)(text); + return (host.createHash ?? generateDjb2Hash)(text); function flattenDiagnosticMessageText(diagnostic: string | DiagnosticMessageChain | undefined): string { return isString(diagnostic) ? @@ -1242,13 +1219,17 @@ export function computeSignatureWithDiagnostics( function locationInfo(diagnostic: DiagnosticWithLocation) { if (diagnostic.file.resolvedPath === sourceFile.resolvedPath) return `(${diagnostic.start},${diagnostic.length})`; if (sourceFileDirectory === undefined) sourceFileDirectory = getDirectoryPath(sourceFile.resolvedPath); - return `${ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceFileDirectory, diagnostic.file.resolvedPath, getCanonicalFileName))}(${diagnostic.start},${diagnostic.length})`; + return `${ensurePathIsNonModuleName(getRelativePathFromDirectory( + sourceFileDirectory, + diagnostic.file.resolvedPath, + getCanonicalFileName ??= createGetCanonicalFileName(program.useCaseSensitiveFileNames()) + ))}(${diagnostic.start},${diagnostic.length})`; } } /** @internal */ -export function computeSignature(text: string, computeHash: BuilderState.ComputeHash | undefined, data?: WriteFileCallbackData) { - return (computeHash ?? generateDjb2Hash)(getTextHandlingSourceMapForSignature(text, data)); +export function computeSignature(text: string, host: HostForComputeHash, data?: WriteFileCallbackData) { + return (host.createHash ?? generateDjb2Hash)(getTextHandlingSourceMapForSignature(text, data)); } /** @internal */ @@ -1265,16 +1246,8 @@ export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, hos return oldProgram; } - /** - * Create the canonical file name for identity - */ - const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); - /** - * Computing hash to for signature verification - */ - const computeHash = maybeBind(host, host.createHash); - const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState, host.disableUseFileVersionAsSignature); - newProgram.getBuildInfo = bundle => getBuildInfo(state, getCanonicalFileName, bundle); + const state = createBuilderProgramState(newProgram, oldState, host.disableUseFileVersionAsSignature); + newProgram.getBuildInfo = bundle => getBuildInfo(state, bundle); // To ensure that we arent storing any references to old program or new program without state newProgram = undefined!; // TODO: GH#18217 @@ -1321,7 +1294,7 @@ export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, hos * in that order would be used to write the files */ function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult { - let affected = getNextAffectedFile(state, cancellationToken, computeHash, getCanonicalFileName, host); + let affected = getNextAffectedFile(state, cancellationToken, host); const programEmitKind = getBuilderFileEmit(state.compilerOptions); let emitKind: BuilderFileEmit = emitOnlyDtsFiles ? programEmitKind & BuilderFileEmit.AllDts : programEmitKind; @@ -1402,10 +1375,10 @@ export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, hos const info = state.fileInfos.get(file.resolvedPath)!; if (info.signature === file.version) { const signature = computeSignatureWithDiagnostics( + state.program!, file, text, - computeHash, - getCanonicalFileName, + host, data, ); // With d.ts diagnostics they are also part of the signature so emitSignature will be different from it since its just hash of d.ts @@ -1454,7 +1427,7 @@ export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, hos */ function handleNewSignature(oldSignatureFormat: EmitSignature | undefined, newSignature: string | undefined) { const oldSignature = !oldSignatureFormat || isString(oldSignatureFormat) ? oldSignatureFormat : oldSignatureFormat[0]; - newSignature ??= computeSignature(text, computeHash, data); + newSignature ??= computeSignature(text, host, data); // Dont write dts files if they didn't change if (newSignature === oldSignature) { // If the signature was encoded as string the dts map options match so nothing to do @@ -1534,7 +1507,7 @@ export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, hos */ function getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult { while (true) { - const affected = getNextAffectedFile(state, cancellationToken, computeHash, getCanonicalFileName, host); + const affected = getNextAffectedFile(state, cancellationToken, host); let result; if (!affected) return undefined; // Done else if (affected !== state.program) { diff --git a/src/compiler/builderPublic.ts b/src/compiler/builderPublic.ts index 89c605eee37..b2893c4691d 100644 --- a/src/compiler/builderPublic.ts +++ b/src/compiler/builderPublic.ts @@ -8,10 +8,6 @@ import { export type AffectedFileResult = { result: T; affected: SourceFile | Program; } | undefined; export interface BuilderProgramHost { - /** - * return true if file names are treated with case sensitivity - */ - useCaseSensitiveFileNames(): boolean; /** * If provided this would be used this hash instead of actual file shape text for detecting changes */ @@ -33,14 +29,11 @@ export interface BuilderProgramHost { * @internal */ storeFilesChangingSignatureDuringEmit?: boolean; - /** - * Gets the current time - * - * @internal - */ - now?(): Date; } +/** @internal */ +export type HostForComputeHash = Pick; + /** * Builder to manage the program state changes */ diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index c76aa7ade75..3fdeb73d1c8 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -1,6 +1,7 @@ import { - arrayFrom, CancellationToken, computeSignatureWithDiagnostics, CustomTransformers, Debug, EmitOutput, emptyArray, + arrayFrom, CancellationToken, computeSignatureWithDiagnostics, createGetCanonicalFileName, CustomTransformers, Debug, EmitOutput, emptyArray, ExportedModulesFromDeclarationEmit, GetCanonicalFileName, getDirectoryPath, getSourceFileOfNode, + HostForComputeHash, isDeclarationFileName, isExternalOrCommonJsModule, isGlobalScopeAugmentation, isJsonSourceFile, isModuleWithStringLiteralName, isStringLiteral, mapDefined, mapDefinedIterator, ModuleDeclaration, ModuleKind, outFile, OutputFile, Path, Program, ResolutionMode, some, SourceFile, StringLiteralLike, Symbol, @@ -158,11 +159,6 @@ export namespace BuilderState { return false; } - /** - * Compute the hash to store the shape of the file - */ - export type ComputeHash = ((data: string) => string) | undefined; - function getReferencedFilesFromImportedModuleSymbol(symbol: Symbol): Path[] { return mapDefined(symbol.declarations, declaration => getSourceFileOfNode(declaration)?.resolvedPath); } @@ -272,7 +268,7 @@ export namespace BuilderState { /** * Creates the state of file references and signature for the new program from oldState if it is safe */ - export function create(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly, disableUseFileVersionAsSignature?: boolean): BuilderState { + export function create(newProgram: Program, oldState?: Readonly, disableUseFileVersionAsSignature?: boolean): BuilderState { const fileInfos = new Map(); const options = newProgram.getCompilerOptions(); const isOutFile = outFile(options); @@ -280,6 +276,7 @@ export namespace BuilderState { createManyToManyPathMap() : undefined; const exportedModulesMap = referencedMap ? createManyToManyPathMap() : undefined; const useOldState = canReuseOldState(referencedMap, oldState); + const getCanonicalFileName = createGetCanonicalFileName(newProgram.useCaseSensitiveFileNames()); // Ensure source files have parent pointers set newProgram.getTypeChecker(); @@ -340,16 +337,14 @@ export namespace BuilderState { programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, - computeHash: ComputeHash, - getCanonicalFileName: GetCanonicalFileName, + host: HostForComputeHash, ): readonly SourceFile[] { const result = getFilesAffectedByWithOldState( state, programOfThisState, path, cancellationToken, - computeHash, - getCanonicalFileName, + host, ); state.oldSignatures?.clear(); state.oldExportedModulesMap?.clear(); @@ -361,19 +356,18 @@ export namespace BuilderState { programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, - computeHash: ComputeHash, - getCanonicalFileName: GetCanonicalFileName, + host: HostForComputeHash, ): readonly SourceFile[] { const sourceFile = programOfThisState.getSourceFileByPath(path); if (!sourceFile) { return emptyArray; } - if (!updateShapeSignature(state, programOfThisState, sourceFile, cancellationToken, computeHash, getCanonicalFileName)) { + if (!updateShapeSignature(state, programOfThisState, sourceFile, cancellationToken, host)) { return [sourceFile]; } - return (state.referencedMap ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit)(state, programOfThisState, sourceFile, cancellationToken, computeHash, getCanonicalFileName); + return (state.referencedMap ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit)(state, programOfThisState, sourceFile, cancellationToken, host); } export function updateSignatureOfFile(state: BuilderState, signature: string | undefined, path: Path) { @@ -389,9 +383,8 @@ export namespace BuilderState { programOfThisState: Program, sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, - computeHash: ComputeHash, - getCanonicalFileName: GetCanonicalFileName, - useFileVersionAsSignature = state.useFileVersionAsSignature + host: HostForComputeHash, + useFileVersionAsSignature = state.useFileVersionAsSignature, ) { // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate if (state.hasCalledUpdateShapeSignature?.has(sourceFile.resolvedPath)) return false; @@ -405,10 +398,10 @@ export namespace BuilderState { (fileName, text, _writeByteOrderMark, _onError, sourceFiles, data) => { Debug.assert(isDeclarationFileName(fileName), `File extension for signature expected to be dts: Got:: ${fileName}`); latestSignature = computeSignatureWithDiagnostics( + programOfThisState, sourceFile, text, - computeHash, - getCanonicalFileName, + host, data, ); if (latestSignature !== prevSignature) { @@ -604,8 +597,7 @@ export namespace BuilderState { programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cancellationToken: CancellationToken | undefined, - computeHash: ComputeHash, - getCanonicalFileName: GetCanonicalFileName, + host: HostForComputeHash, ) { if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) { return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape); @@ -629,7 +621,7 @@ export namespace BuilderState { if (!seenFileNamesMap.has(currentPath)) { const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath)!; seenFileNamesMap.set(currentPath, currentSourceFile); - if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cancellationToken, computeHash, getCanonicalFileName)) { + if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cancellationToken, host)) { queue.push(...getReferencedByPaths(state, currentSourceFile.resolvedPath)); } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 521588614c7..ebc9bcaec99 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -637,7 +637,7 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi if (sourceMapFilePath) { const sourceMap = sourceMapGenerator.toString(); writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap, /*writeByteOrderMark*/ false, sourceFiles); - if (printer.bundleFileInfo) printer.bundleFileInfo.mapHash = computeSignature(sourceMap, maybeBind(host, host.createHash)); + if (printer.bundleFileInfo) printer.bundleFileInfo.mapHash = computeSignature(sourceMap, host); } } else { @@ -649,7 +649,7 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi writeFile(host, emitterDiagnostics, jsFilePath, text, !!compilerOptions.emitBOM, sourceFiles, { sourceMapUrlPos, diagnostics: transform.diagnostics }); // We store the hash of the text written in the buildinfo to ensure that text of the referenced d.ts file is same as whats in the buildinfo // This is needed because incremental can be toggled between two runs and we might use stale file text to do text manipulation in prepend mode - if (printer.bundleFileInfo) printer.bundleFileInfo.hash = computeSignature(text, maybeBind(host, host.createHash)); + if (printer.bundleFileInfo) printer.bundleFileInfo.hash = computeSignature(text, host); // Reset state writer.clear(); @@ -846,7 +846,6 @@ function emitUsingBuildInfoWorker( getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined, customTransformers?: CustomTransformers ): EmitUsingBuildInfoResult { - const createHash = maybeBind(host, host.createHash); const { buildInfoPath, jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath } = getOutputPathsForBundle(config.options, /*forceDtsPaths*/ false); // If host directly provides buildinfo we can get it directly. This allows host to cache the buildinfo const buildInfo = host.getBuildInfo!(buildInfoPath!, config.options.configFilePath); @@ -856,20 +855,20 @@ function emitUsingBuildInfoWorker( const jsFileText = host.readFile(Debug.checkDefined(jsFilePath)); if (!jsFileText) return jsFilePath!; // If the jsFileText is not same has what it was created with, tsbuildinfo is stale so dont use it - if (computeSignature(jsFileText, createHash) !== buildInfo.bundle.js.hash) return jsFilePath!; + if (computeSignature(jsFileText, host) !== buildInfo.bundle.js.hash) return jsFilePath!; const sourceMapText = sourceMapFilePath && host.readFile(sourceMapFilePath); // error if no source map or for now if inline sourcemap if ((sourceMapFilePath && !sourceMapText) || config.options.inlineSourceMap) return sourceMapFilePath || "inline sourcemap decoding"; - if (sourceMapFilePath && computeSignature(sourceMapText!, createHash) !== buildInfo.bundle.js.mapHash) return sourceMapFilePath; + if (sourceMapFilePath && computeSignature(sourceMapText!, host) !== buildInfo.bundle.js.mapHash) return sourceMapFilePath; // read declaration text const declarationText = declarationFilePath && host.readFile(declarationFilePath); if (declarationFilePath && !declarationText) return declarationFilePath; - if (declarationFilePath && computeSignature(declarationText!, createHash) !== buildInfo.bundle.dts!.hash) return declarationFilePath; + if (declarationFilePath && computeSignature(declarationText!, host) !== buildInfo.bundle.dts!.hash) return declarationFilePath; const declarationMapText = declarationMapPath && host.readFile(declarationMapPath); // error if no source map or for now if inline sourcemap if ((declarationMapPath && !declarationMapText) || config.options.inlineSourceMap) return declarationMapPath || "inline sourcemap decoding"; - if (declarationMapPath && computeSignature(declarationMapText!, createHash) !== buildInfo.bundle.dts!.mapHash) return declarationMapPath; + if (declarationMapPath && computeSignature(declarationMapText!, host) !== buildInfo.bundle.dts!.mapHash) return declarationMapPath; const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath!, host.getCurrentDirectory())); const ownPrependInput = createInputFilesWithFileTexts( @@ -936,7 +935,7 @@ function emitUsingBuildInfoWorker( const program = buildInfo.program; if (program && changedDtsText !== undefined && config.options.composite) { // Update the output signature - (program as ProgramBundleEmitBuildInfo).outSignature = computeSignature(changedDtsText, createHash, changedDtsData); + (program as ProgramBundleEmitBuildInfo).outSignature = computeSignature(changedDtsText, host, changedDtsData); } // Update sourceFileInfo const { js, dts, sourceFiles } = buildInfo.bundle!; @@ -950,7 +949,7 @@ function emitUsingBuildInfoWorker( getSourceFileFromReference: returnUndefined, redirectTargetsMap: createMultiMap(), getFileIncludeReasons: notImplemented, - createHash, + createHash: maybeBind(host, host.createHash), }; emitFiles( notImplementedResolver, diff --git a/src/server/project.ts b/src/server/project.ts index e2010c116b3..d34b2e0e83f 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -332,6 +332,8 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo autoImportProviderHost: AutoImportProviderProject | false | undefined; /** @internal */ protected typeAcquisition: TypeAcquisition | undefined; + /** @internal */ + createHash = maybeBind(this.projectService.host, this.projectService.host.createHash); /** @internal */ constructor( @@ -740,15 +742,14 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo return []; } updateProjectIfDirty(this); - this.builderState = BuilderState.create(this.program!, this.projectService.toCanonicalFileName, this.builderState, /*disableUseFileVersionAsSignature*/ true); + this.builderState = BuilderState.create(this.program!, this.builderState, /*disableUseFileVersionAsSignature*/ true); return mapDefined( BuilderState.getFilesAffectedBy( this.builderState, this.program!, scriptInfo.path, this.cancellationToken, - maybeBind(this.projectService.host, this.projectService.host.createHash), - this.getCanonicalFileName, + this.projectService.host, ), sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined ); diff --git a/src/services/services.ts b/src/services/services.ts index 703b169d60d..ac4c3bb15c1 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1398,6 +1398,7 @@ export function createLanguageService( trace: maybeBind(host, host.trace), resolveModuleNames: maybeBind(host, host.resolveModuleNames), getModuleResolutionCache: maybeBind(host, host.getModuleResolutionCache), + createHash: maybeBind(host, host.createHash), resolveTypeReferenceDirectives: maybeBind(host, host.resolveTypeReferenceDirectives), useSourceOfProjectReferenceRedirect: maybeBind(host, host.useSourceOfProjectReferenceRedirect), getParsedCommandLine, diff --git a/src/services/types.ts b/src/services/types.ts index ce1fafb1715..22a3c4a1f76 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -303,6 +303,7 @@ export interface LanguageServiceHost extends GetEffectiveTypeRootsHost, MinimalR */ readDirectory?(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[]; realpath?(path: string): string; + /** @internal */ createHash?(data: string): string; /* * Unlike `realpath and `readDirectory`, `readFile` and `fileExists` are now _required_ diff --git a/src/testRunner/unittests/builder.ts b/src/testRunner/unittests/builder.ts index 0c3a8535a9d..8ac912824f3 100644 --- a/src/testRunner/unittests/builder.ts +++ b/src/testRunner/unittests/builder.ts @@ -74,7 +74,7 @@ describe("unittests:: builder", () => { }); function makeAssertChanges(getProgram: () => ts.Program): (fileNames: readonly string[]) => void { - const host: ts.BuilderProgramHost = { useCaseSensitiveFileNames: ts.returnTrue }; + const host: ts.BuilderProgramHost = { }; let builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram | undefined; return fileNames => { const program = getProgram(); @@ -88,7 +88,7 @@ function makeAssertChanges(getProgram: () => ts.Program): (fileNames: readonly s } function makeAssertChangesWithCancellationToken(getProgram: () => ts.Program): (fileNames: readonly string[], cancelAfterEmitLength?: number) => void { - const host: ts.BuilderProgramHost = { useCaseSensitiveFileNames: ts.returnTrue }; + const host: ts.BuilderProgramHost = { }; let builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram | undefined; let cancel = false; const cancellationToken: ts.CancellationToken = { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index e839ea493f4..9753bb732a3 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -9431,10 +9431,6 @@ declare namespace ts { affected: SourceFile | Program; } | undefined; interface BuilderProgramHost { - /** - * return true if file names are treated with case sensitivity - */ - useCaseSensitiveFileNames(): boolean; /** * If provided this would be used this hash instead of actual file shape text for detecting changes */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 5fc30dc5798..e92cfcac526 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5495,10 +5495,6 @@ declare namespace ts { affected: SourceFile | Program; } | undefined; interface BuilderProgramHost { - /** - * return true if file names are treated with case sensitivity - */ - useCaseSensitiveFileNames(): boolean; /** * If provided this would be used this hash instead of actual file shape text for detecting changes */