Skip to content

Commit 5553f36

Browse files
committed
Instead of queueing build for downstream projects right when invalidating project, do it after build for invalidated project is complete
1 parent bdf1c78 commit 5553f36

File tree

2 files changed

+38
-29
lines changed

2 files changed

+38
-29
lines changed

src/compiler/tsbuild.ts

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -783,10 +783,7 @@ namespace ts {
783783
diagnostics.removeKey(resolved);
784784
}
785785

786-
if (addProjToQueue(resolved, reloadLevel)) {
787-
// TODO: instead of adding the dependent project to queue right away postpone this
788-
queueBuildForDownstreamReferences(resolved);
789-
}
786+
addProjToQueue(resolved, reloadLevel);
790787
}
791788

792789
/**
@@ -797,10 +794,8 @@ namespace ts {
797794
if (value === undefined) {
798795
projectPendingBuild.setValue(proj, reloadLevel || ConfigFileProgramReloadLevel.None);
799796
invalidatedProjectQueue.push(proj);
800-
return true;
801797
}
802-
803-
if (value < (reloadLevel || ConfigFileProgramReloadLevel.None)) {
798+
else if (value < (reloadLevel || ConfigFileProgramReloadLevel.None)) {
804799
projectPendingBuild.setValue(proj, reloadLevel || ConfigFileProgramReloadLevel.None);
805800
}
806801
}
@@ -823,20 +818,6 @@ namespace ts {
823818
return !!projectPendingBuild.getSize();
824819
}
825820

826-
// Mark all downstream projects of this one needing to be built "later"
827-
function queueBuildForDownstreamReferences(root: ResolvedConfigFileName) {
828-
const dependencyGraph = getGlobalDependencyGraph();
829-
const referencingProjects = dependencyGraph.referencingProjectsMap.getValue(root);
830-
if (!referencingProjects) return;
831-
// Always use build order to queue projects
832-
for (const project of dependencyGraph.buildQueue) {
833-
// Can skip circular references
834-
if (referencingProjects.hasKey(project) && addProjToQueue(project)) {
835-
queueBuildForDownstreamReferences(project);
836-
}
837-
}
838-
}
839-
840821
function scheduleBuildInvalidatedProject() {
841822
if (!hostWithWatch.setTimeout || !hostWithWatch.clearTimeout) {
842823
return;
@@ -910,7 +891,20 @@ namespace ts {
910891
return;
911892
}
912893

913-
buildSingleProject(resolved);
894+
const buildResult = buildSingleProject(resolved);
895+
// If declaration output changed then only queue in build for downstream projects
896+
if (!(buildResult & BuildResultFlags.DeclarationOutputUnchanged)) {
897+
const dependencyGraph = getGlobalDependencyGraph();
898+
const referencingProjects = dependencyGraph.referencingProjectsMap.getValue(resolved);
899+
if (!referencingProjects) return;
900+
// Always use build order to queue projects
901+
for (const project of dependencyGraph.buildQueue) {
902+
// Can skip circular references
903+
if (referencingProjects.hasKey(project)) {
904+
addProjToQueue(project);
905+
}
906+
}
907+
}
914908
}
915909

916910
function createDependencyGraph(roots: ResolvedConfigFileName[]): DependencyGraph {
@@ -928,7 +922,7 @@ namespace ts {
928922
referencingProjectsMap
929923
};
930924

931-
function visit(projPath: ResolvedConfigFileName, inCircularContext = false) {
925+
function visit(projPath: ResolvedConfigFileName, inCircularContext?: boolean) {
932926
// Already visited
933927
if (permanentMarks.hasKey(projPath)) return;
934928
// Circular
@@ -1032,14 +1026,13 @@ namespace ts {
10321026
let anyDtsChanged = false;
10331027
program.emit(/*targetSourceFile*/ undefined, (fileName, content, writeBom, onError) => {
10341028
let priorChangeTime: Date | undefined;
1035-
1036-
if (!anyDtsChanged && isDeclarationFile(fileName) && host.fileExists(fileName)) {
1037-
if (host.readFile(fileName) === content) {
1038-
// Check for unchanged .d.ts files
1039-
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
1029+
if (!anyDtsChanged && isDeclarationFile(fileName)) {
1030+
// Check for unchanged .d.ts files
1031+
if (host.fileExists(fileName) && host.readFile(fileName) === content) {
10401032
priorChangeTime = host.getModifiedTime(fileName);
10411033
}
10421034
else {
1035+
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
10431036
anyDtsChanged = true;
10441037
}
10451038
}

src/testRunner/unittests/tsbuild.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,26 @@ namespace ts {
210210
assert.equal(fs.statSync("/src/logic/index.js").mtimeMs, time(), "JS file should have been rebuilt");
211211
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should *not* have been rebuilt");
212212

213+
// Does not build tests or core because there is no change in declaration file
214+
tick();
215+
builder.buildInvalidatedProject();
216+
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should have been rebuilt");
217+
assert.isBelow(fs.statSync("/src/core/index.js").mtimeMs, time(), "Upstream JS file should not have been rebuilt");
218+
219+
// Rebuild this project
220+
tick();
221+
fs.writeFileSync("/src/logic/index.ts", `${fs.readFileSync("/src/logic/index.ts")}
222+
export class cNew {}`);
223+
builder.invalidateProject("/src/logic");
224+
builder.buildInvalidatedProject();
225+
// The file should be updated
226+
assert.equal(fs.statSync("/src/logic/index.js").mtimeMs, time(), "JS file should have been rebuilt");
227+
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should *not* have been rebuilt");
228+
213229
// Build downstream projects should update 'tests', but not 'core'
214230
tick();
215231
builder.buildInvalidatedProject();
216-
assert.equal(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should have been rebuilt");
232+
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should have been rebuilt");
217233
assert.isBelow(fs.statSync("/src/core/index.js").mtimeMs, time(), "Upstream JS file should not have been rebuilt");
218234
});
219235
});

0 commit comments

Comments
 (0)