Skip to content

improvement(fluid-build): Ignore some tsconfig fields when determining incremental build #23197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build-tools/packages/build-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"ignore": "^5.3.2",
"json5": "^2.2.3",
"lodash.isequal": "^4.5.0",
"microdiff": "^1.5.0",
"multimatch": "^5.0.0",
"picocolors": "^1.1.1",
"picomatch": "^2.3.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { Task, TaskExec } from "../task";

const { log } = defaultLogger;
const traceTaskTrigger = registerDebug("fluid-build:task:trigger");
const traceTaskTriggerDebug = registerDebug("fluid-build:task:trigger:debug");
const traceTaskCheck = registerDebug("fluid-build:task:check");
const traceTaskInitDep = registerDebug("fluid-build:task:init:dep");
const traceTaskInitWeight = registerDebug("fluid-build:task:init:weight");
Expand Down Expand Up @@ -435,6 +436,11 @@ export abstract class LeafTask extends Task {
traceTaskTrigger(msg);
}

protected traceTriggerDebug(reason: string) {
const msg = `${this.nameColored}: [${reason}]`;
traceTaskTriggerDebug(msg);
}

protected traceError(msg: string) {
traceError(`${this.nameColored}: ${msg}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
* Licensed under the MIT License.
*/

import * as assert from "node:assert";
import assert from "node:assert";
import { type BigIntStats, type Stats, existsSync, lstatSync } from "node:fs";
import { readFile } from "node:fs/promises";
import path from "node:path";

import isEqual from "lodash.isequal";
import diff from "microdiff";
import * as tsTypes from "typescript";

import { TscUtil, getTscUtils } from "../../tscUtils";
Expand Down Expand Up @@ -258,12 +260,37 @@ export class TscTask extends LeafTask {
path.resolve,
);

// The following code checks that the cached buildInfo options -- that is, the tsconfig options used to build
// originally -- match the current tsconfig. If they don't match, then the cache is outdated and a rebuild is
// needed.
//
// However, there are some tsconfig settings that don't seem to be cached in the tsbuildinfo. This makes any builds
// including these settings always fail this check. We special-case the properties in this set -- any differences in
// those fields are ignored.
//
// IMPORTANT: This is a somewhat unsafe action. Technically speaking, we don't know for sure the incremental build
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On one hand, is anyone going to commonly change allowJs/checkJs and then re-run and incremental build? On the other hand, why is this opt-out instead of opt-in if it is potentially unsafe?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On one hand, is anyone going to commonly change allowJs/checkJs and then re-run and incremental build?

This isn't quite the situation. Without this change, projects like property-properties and ai-collab have builds that are effectively never cached, regardless of whether people change those projects' settings.

This is because we use the on-disk tsconfig settings to compare with what the tsbuildInfo has. Any project with those settings in their tsconfig is always going to build.

Fortunately these projects are sort of at the edge of the build graph, so most people's day-to-day is not impacted. However, I monitor the cached full build time informally over time, and this is one of the last remaining issues to get to a 100% cached full build. I'd like to get there almost out of spite to the universe at this point. 😆

On the other hand, why is this opt-out instead of opt-in if it is potentially unsafe?

Good point. I remember being proud that I had added an escape hatch, which we've never done with any build-tools changes. But in retrospect I chose a safER option but not the safEST option. I'll make it opt-in.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make it opt-in.

Done in latest.

// status when there is ANY difference between these settings. Thus the safest thing to do is to assume a rebuild is
// needed, Projects using these ignored settings may exhibit strange incremental build behavior.
//
// For that reason, this behavior must be enabled using the _FLUID_BUILD_ENABLE_IGNORE_TSC_OPTIONS_
// environment variable. To enable ignoring these settings, set the environment variable to "1".
const tsConfigOptionsIgnored: Set<string> =
process.env._FLUID_BUILD_ENABLE_IGNORE_TSC_OPTIONS_ === "1"
? new Set(["allowJs", "checkJs"])
: new Set();

for (const ignoredOption of tsConfigOptionsIgnored) {
// Delete the ignored option if it exists on the object
if (ignoredOption in tsBuildInfoOptions) {
this.traceTrigger(`ignoring tsBuildInfoOption: '${ignoredOption}'`);
delete tsBuildInfoOptions[ignoredOption];
}
}

if (!isEqual(configOptions, tsBuildInfoOptions)) {
this.traceTrigger(`ts option changed ${configFileFullPath}`);
this.traceTrigger("Config:");
this.traceTrigger(JSON.stringify(configOptions, undefined, 2));
this.traceTrigger("BuildInfo:");
this.traceTrigger(JSON.stringify(tsBuildInfoOptions, undefined, 2));
this.traceTrigger(`ts option changed in '${configFileFullPath}'`);
this.traceTrigger("Diff:");
this.traceTrigger(JSON.stringify(diff(configOptions, tsBuildInfoOptions), undefined, 2));
return false;
}
return true;
Expand Down
18 changes: 13 additions & 5 deletions build-tools/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.