fix(bundling): skip unnecessary type-check in TS Solution Setup when skipTypeCheck is true#34493
Conversation
✅ Deploy Preview for nx-docs canceled.
|
✅ Deploy Preview for nx-dev canceled.
|
…kipTypeCheck is true In TS Solution Setup, the esbuild executor forces runTypeCheck even when skipTypeCheck: true and declaration: false. This runs a type check in noEmit mode that suppresses all diagnostics (via ignoreDiagnostics: true), making it completely pointless -- except for its harmful side effect of writing a poisoned 19-byte tsbuildinfo file that causes race conditions with tsc --build. Primary fix (esbuild.impl.ts): Only force type-check in TS Solution Setup when declarations actually need to be generated (isTsSolutionSetup && declaration). When skipTypeCheck is true and declaration is false, the type check is skipped entirely. Defense-in-depth (run-type-check.ts): Also set composite: false in the noEmit code path to prevent tsbuildinfo writes from any caller, since composite: true + noEmit: true still triggers TypeScript to write a minimal tsbuildinfo file.
0714e5c to 505473e Compare | View your CI Pipeline Execution ↗ for commit 5c079b0
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Important
At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.
Nx Cloud is proposing a fix for your failed CI:
Our changes apply Prettier formatting to the conditional statement in esbuild.impl.ts that was modified in the previous commit. The long conditional checking skipTypeCheck and isTsSolutionSetup has been reformatted to span multiple lines, ensuring compliance with the project's code style standards and fixing the format:check failure.
Tip
✅ We verified this fix by re-running nx-cloud record -- nx format:check.
Suggested Fix changes
diff --git a/packages/esbuild/src/executors/esbuild/esbuild.impl.ts b/packages/esbuild/src/executors/esbuild/esbuild.impl.ts index dd47fba365..f92c181e70 100644 --- a/packages/esbuild/src/executors/esbuild/esbuild.impl.ts +++ b/packages/esbuild/src/executors/esbuild/esbuild.impl.ts @@ -192,7 +192,10 @@ export async function* esbuildExecutor( ); } else { // Run type-checks first and bail if they don't pass. - if (!options.skipTypeCheck || (options.isTsSolutionSetup && options.declaration)) { + if ( + !options.skipTypeCheck || + (options.isTsSolutionSetup && options.declaration) + ) { const { errors } = await runTypeCheck(options, context); if (errors.length > 0) { yield { success: false }; 🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.
Because this branch comes from a fork, it is not possible for us to apply fixes directly, but you can apply the changes locally using the available options below.
Apply changes locally with:
npx nx-cloud apply-locally U7b9-UZbX Apply fix locally with your editor ↗ View interactive diff ↗
🎓 Learn more about Self-Healing CI on nx.dev
…skipTypeCheck is true (#34493) ## Current Behavior In TS Solution Setup, the esbuild executor forces `runTypeCheck` even when `skipTypeCheck: true` and `declaration: false`, due to the `|| options.isTsSolutionSetup` condition in `esbuild.impl.ts` (lines 139-140 and 195). When `declaration: false`, this type check runs in `noEmit` mode with `ignoreDiagnostics: true` — making it **completely pointless** (no declarations emitted, no diagnostics reported). Its only observable effect is writing a poisoned 19-byte tsbuildinfo file that causes race conditions with `tsc --build`. ## Expected Behavior When `skipTypeCheck: true` and `declaration: false`, the esbuild executor should not run type checking at all. The `isTsSolutionSetup` override should only force type checking when declarations actually need to be generated. ## Fix ### Primary: Skip unnecessary type check (`esbuild.impl.ts`) ```diff // Non-watch mode (line 195) - if (!options.skipTypeCheck || options.isTsSolutionSetup) { + if (!options.skipTypeCheck || (options.isTsSolutionSetup && options.declaration)) { // Watch mode (lines 139-140) - options.isTsSolutionSetup + (options.isTsSolutionSetup && options.declaration) ``` Only force type checking in TS Solution Setup when declarations need to be generated. This eliminates the pointless type check entirely. ### Defense-in-depth: Prevent tsbuildinfo in `noEmit` mode (`run-type-check.ts`) ```diff - : { noEmit: true }; + : { noEmit: true, composite: false }; ``` Setting `composite: false` alongside `noEmit: true` prevents TypeScript from writing tsbuildinfo files, protecting against this class of bug from any caller of `runTypeCheck`. ## Why This is Safe | Scenario | Before | After | |----------|--------|-------| | `skipTypeCheck: false`, `declaration: false`, `isTsSolutionSetup: true` | Runs type check (noEmit) | Still runs (`!false \|\| ...` = true) | | `skipTypeCheck: false`, `declaration: true`, `isTsSolutionSetup: true` | Runs type check (emitDeclarationOnly) | Still runs | | `skipTypeCheck: true`, `declaration: true`, `isTsSolutionSetup: true` | normalize.ts overrides skipTypeCheck to false; runs type check | Still runs (same normalization) | | **`skipTypeCheck: true`, `declaration: false`, `isTsSolutionSetup: true`** | **Runs pointless type check (noEmit + ignoreDiagnostics), writes poisoned tsbuildinfo** | **Skipped entirely** | The only behavior change is in the last row — the case where the type check was doing nothing useful but causing harm. ## Related Issue(s) Fixes #34492 --------- Co-authored-by: Jack Hsu <jack.hsu@gmail.com> (cherry picked from commit bdeeb03)
…skipTypeCheck is true (#34493) ## Current Behavior In TS Solution Setup, the esbuild executor forces `runTypeCheck` even when `skipTypeCheck: true` and `declaration: false`, due to the `|| options.isTsSolutionSetup` condition in `esbuild.impl.ts` (lines 139-140 and 195). When `declaration: false`, this type check runs in `noEmit` mode with `ignoreDiagnostics: true` — making it **completely pointless** (no declarations emitted, no diagnostics reported). Its only observable effect is writing a poisoned 19-byte tsbuildinfo file that causes race conditions with `tsc --build`. ## Expected Behavior When `skipTypeCheck: true` and `declaration: false`, the esbuild executor should not run type checking at all. The `isTsSolutionSetup` override should only force type checking when declarations actually need to be generated. ## Fix ### Primary: Skip unnecessary type check (`esbuild.impl.ts`) ```diff // Non-watch mode (line 195) - if (!options.skipTypeCheck || options.isTsSolutionSetup) { + if (!options.skipTypeCheck || (options.isTsSolutionSetup && options.declaration)) { // Watch mode (lines 139-140) - options.isTsSolutionSetup + (options.isTsSolutionSetup && options.declaration) ``` Only force type checking in TS Solution Setup when declarations need to be generated. This eliminates the pointless type check entirely. ### Defense-in-depth: Prevent tsbuildinfo in `noEmit` mode (`run-type-check.ts`) ```diff - : { noEmit: true }; + : { noEmit: true, composite: false }; ``` Setting `composite: false` alongside `noEmit: true` prevents TypeScript from writing tsbuildinfo files, protecting against this class of bug from any caller of `runTypeCheck`. ## Why This is Safe | Scenario | Before | After | |----------|--------|-------| | `skipTypeCheck: false`, `declaration: false`, `isTsSolutionSetup: true` | Runs type check (noEmit) | Still runs (`!false \|\| ...` = true) | | `skipTypeCheck: false`, `declaration: true`, `isTsSolutionSetup: true` | Runs type check (emitDeclarationOnly) | Still runs | | `skipTypeCheck: true`, `declaration: true`, `isTsSolutionSetup: true` | normalize.ts overrides skipTypeCheck to false; runs type check | Still runs (same normalization) | | **`skipTypeCheck: true`, `declaration: false`, `isTsSolutionSetup: true`** | **Runs pointless type check (noEmit + ignoreDiagnostics), writes poisoned tsbuildinfo** | **Skipped entirely** | The only behavior change is in the last row — the case where the type check was doing nothing useful but causing harm. ## Related Issue(s) Fixes #34492 --------- Co-authored-by: Jack Hsu <jack.hsu@gmail.com> (cherry picked from commit bdeeb03)
| This pull request has already been merged/closed. If you experience issues related to these changes, please open a new issue referencing this pull request. |
Current Behavior
In TS Solution Setup, the esbuild executor forces
runTypeCheckeven whenskipTypeCheck: trueanddeclaration: false, due to the|| options.isTsSolutionSetupcondition inesbuild.impl.ts(lines 139-140 and 195).When
declaration: false, this type check runs innoEmitmode withignoreDiagnostics: true— making it completely pointless (no declarations emitted, no diagnostics reported). Its only observable effect is writing a poisoned 19-byte tsbuildinfo file that causes race conditions withtsc --build.Expected Behavior
When
skipTypeCheck: trueanddeclaration: false, the esbuild executor should not run type checking at all. TheisTsSolutionSetupoverride should only force type checking when declarations actually need to be generated.Fix
Primary: Skip unnecessary type check (
esbuild.impl.ts)Only force type checking in TS Solution Setup when declarations need to be generated. This eliminates the pointless type check entirely.
Defense-in-depth: Prevent tsbuildinfo in
noEmitmode (run-type-check.ts)Setting
composite: falsealongsidenoEmit: trueprevents TypeScript from writing tsbuildinfo files, protecting against this class of bug from any caller ofrunTypeCheck.Why This is Safe
skipTypeCheck: false,declaration: false,isTsSolutionSetup: true!false || ...= true)skipTypeCheck: false,declaration: true,isTsSolutionSetup: trueskipTypeCheck: true,declaration: true,isTsSolutionSetup: trueskipTypeCheck: true,declaration: false,isTsSolutionSetup: trueThe only behavior change is in the last row — the case where the type check was doing nothing useful but causing harm.
Related Issue(s)
Fixes #34492