mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #21149 from Microsoft/tweakRecompileConditions
Do not trigger resolution cache invalidation if watch is triggered by program file emit
This commit is contained in:
+10
-4
@@ -18,8 +18,8 @@ namespace ts {
|
||||
* If an array, the full list of source files to emit.
|
||||
* Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit.
|
||||
*/
|
||||
export function forEachEmittedFile(
|
||||
host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean) => void,
|
||||
export function forEachEmittedFile<T>(
|
||||
host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean) => T,
|
||||
sourceFilesOrTargetSourceFile?: SourceFile[] | SourceFile,
|
||||
emitOnlyDtsFiles?: boolean) {
|
||||
|
||||
@@ -30,7 +30,10 @@ namespace ts {
|
||||
const jsFilePath = options.outFile || options.out;
|
||||
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
|
||||
const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + Extension.Dts : "";
|
||||
action({ jsFilePath, sourceMapFilePath, declarationFilePath }, createBundle(sourceFiles), emitOnlyDtsFiles);
|
||||
const result = action({ jsFilePath, sourceMapFilePath, declarationFilePath }, createBundle(sourceFiles), emitOnlyDtsFiles);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -38,7 +41,10 @@ namespace ts {
|
||||
const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options));
|
||||
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
|
||||
const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
|
||||
action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFile, emitOnlyDtsFiles);
|
||||
const result = action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFile, emitOnlyDtsFiles);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-1
@@ -667,7 +667,8 @@ namespace ts {
|
||||
dropDiagnosticsProducingTypeChecker,
|
||||
getSourceFileFromReference,
|
||||
sourceFileToPackageName,
|
||||
redirectTargetsSet
|
||||
redirectTargetsSet,
|
||||
isEmittedFile
|
||||
};
|
||||
|
||||
verifyCompilerOptions();
|
||||
@@ -2343,6 +2344,20 @@ namespace ts {
|
||||
hasEmitBlockingDiagnostics.set(toPath(emitFileName), true);
|
||||
programDiagnostics.add(diag);
|
||||
}
|
||||
|
||||
function isEmittedFile(file: string) {
|
||||
if (options.noEmit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return forEachEmittedFile(getEmitHost(), ({ jsFilePath, declarationFilePath }) =>
|
||||
isSameFile(jsFilePath, file) ||
|
||||
(declarationFilePath && isSameFile(declarationFilePath, file)));
|
||||
}
|
||||
|
||||
function isSameFile(file1: string, file2: string) {
|
||||
return comparePaths(file1, file2, currentDirectory, !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace ts {
|
||||
getGlobalCache?(): string | undefined;
|
||||
writeLog(s: string): void;
|
||||
maxNumberOfFilesToIterateForInvalidation?: number;
|
||||
getCurrentProgram(): Program;
|
||||
}
|
||||
|
||||
interface DirectoryWatchesOfFailedLookup {
|
||||
@@ -472,6 +473,11 @@ namespace ts {
|
||||
resolutionHost.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
|
||||
}
|
||||
|
||||
// Ignore emits from the program
|
||||
if (isEmittedFileOfProgram(resolutionHost.getCurrentProgram(), fileOrDirectory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the files are added to project root or node_modules directory, always run through the invalidation process
|
||||
// Otherwise run through invalidation only if adding to the immediate directory
|
||||
if (!allFilesHaveInvalidatedResolution &&
|
||||
|
||||
@@ -2659,6 +2659,8 @@ namespace ts {
|
||||
/* @internal */ sourceFileToPackageName: Map<string>;
|
||||
/** Set of all source files that some other source file redirects to. */
|
||||
/* @internal */ redirectTargetsSet: Map<true>;
|
||||
/** Is the file emitted file */
|
||||
/* @internal */ isEmittedFile(file: string): boolean;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
+10
-1
@@ -20,6 +20,9 @@ namespace ts {
|
||||
// Callbacks to do custom action before creating program and after creating program
|
||||
beforeCompile(compilerOptions: CompilerOptions): void;
|
||||
afterCompile(host: DirectoryStructureHost, program: Program, builder: Builder): void;
|
||||
|
||||
// Only for testing
|
||||
maxNumberOfFilesToIterateForInvalidation?: number;
|
||||
}
|
||||
|
||||
const defaultFormatDiagnosticsHost: FormatDiagnosticsHost = sys ? {
|
||||
@@ -301,6 +304,8 @@ namespace ts {
|
||||
hasChangedAutomaticTypeDirectiveNames = true;
|
||||
scheduleProgramUpdate();
|
||||
},
|
||||
maxNumberOfFilesToIterateForInvalidation: watchingHost.maxNumberOfFilesToIterateForInvalidation,
|
||||
getCurrentProgram,
|
||||
writeLog
|
||||
};
|
||||
// Cache for the module resolution
|
||||
@@ -318,7 +323,11 @@ namespace ts {
|
||||
// Update the wild card directory watch
|
||||
watchConfigFileWildCardDirectories();
|
||||
|
||||
return () => program;
|
||||
return getCurrentProgram;
|
||||
|
||||
function getCurrentProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
function synchronizeProgram() {
|
||||
writeLog(`Synchronizing program`);
|
||||
|
||||
@@ -82,6 +82,14 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function isEmittedFileOfProgram(program: Program | undefined, file: string) {
|
||||
if (!program) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return program.isEmittedFile(file);
|
||||
}
|
||||
|
||||
export function addFileWatcher(host: System, file: string, cb: FileWatcherCallback): FileWatcher {
|
||||
return host.watchFile(file, cb);
|
||||
}
|
||||
|
||||
@@ -30,8 +30,9 @@ namespace ts.tscWatch {
|
||||
return ts.parseConfigFile(configFileName, {}, watchingSystemHost.system, watchingSystemHost.reportDiagnostic, watchingSystemHost.reportWatchDiagnostic);
|
||||
}
|
||||
|
||||
function createWatchModeWithConfigFile(configFilePath: string, host: WatchedSystem) {
|
||||
function createWatchModeWithConfigFile(configFilePath: string, host: WatchedSystem, maxNumberOfFilesToIterateForInvalidation?: number) {
|
||||
const watchingSystemHost = createWatchingSystemHost(host);
|
||||
watchingSystemHost.maxNumberOfFilesToIterateForInvalidation = maxNumberOfFilesToIterateForInvalidation;
|
||||
const configFileResult = parseConfigFile(configFilePath, watchingSystemHost);
|
||||
return ts.createWatchModeWithConfigFile(configFileResult, {}, watchingSystemHost);
|
||||
}
|
||||
@@ -1067,6 +1068,36 @@ namespace ts.tscWatch {
|
||||
assert.equal(nowErrors[0].start, intialErrors[0].start - configFileContentComment.length);
|
||||
assert.equal(nowErrors[1].start, intialErrors[1].start - configFileContentComment.length);
|
||||
});
|
||||
|
||||
it("should not trigger recompilation because of program emit", () => {
|
||||
const proj = "/user/username/projects/myproject";
|
||||
const file1: FileOrFolder = {
|
||||
path: `${proj}/file1.ts`,
|
||||
content: "export const c = 30;"
|
||||
};
|
||||
const file2: FileOrFolder = {
|
||||
path: `${proj}/src/file2.ts`,
|
||||
content: `import {c} from "file1"; export const d = 30;`
|
||||
};
|
||||
const tsconfig: FileOrFolder = {
|
||||
path: `${proj}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
module: "amd",
|
||||
outDir: "build"
|
||||
}
|
||||
})
|
||||
};
|
||||
const host = createWatchedSystem([file1, file2, libFile, tsconfig], { currentDirectory: proj });
|
||||
const watch = createWatchModeWithConfigFile(tsconfig.path, host, /*maxNumberOfFilesToIterateForInvalidation*/1);
|
||||
checkProgramActualFiles(watch(), [file1.path, file2.path, libFile.path]);
|
||||
|
||||
assert.isTrue(host.fileExists("build/file1.js"));
|
||||
assert.isTrue(host.fileExists("build/src/file2.js"));
|
||||
|
||||
// This should be 0
|
||||
host.checkTimeoutQueueLengthAndRun(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsc-watch emit with outFile or out setting", () => {
|
||||
|
||||
@@ -830,7 +830,10 @@ namespace ts.server {
|
||||
return !hasChanges;
|
||||
}
|
||||
|
||||
|
||||
/* @internal */
|
||||
getCurrentProgram() {
|
||||
return this.program;
|
||||
}
|
||||
|
||||
protected removeExistingTypings(include: string[]): string[] {
|
||||
const existing = ts.getAutomaticTypeDirectiveNames(this.getCompilerOptions(), this.directoryStructureHost);
|
||||
|
||||
Reference in New Issue
Block a user