Skip to content

Improve responsiveness of tsc build to interruption - #4592

Closed
Luke Sandberg (lukesandberg) wants to merge 14 commits into
microsoft:mainfrom
lukesandberg:lukesandberg/fix_build_cancellation
Closed

Improve responsiveness of tsc build to interruption#4592
Luke Sandberg (lukesandberg) wants to merge 14 commits into
microsoft:mainfrom
lukesandberg:lukesandberg/fix_build_cancellation

Conversation

@lukesandberg

@lukesandberg Luke Sandberg (lukesandberg) commented Jul 10, 2026

Copy link
Copy Markdown

Improve responsiveness of tsc and tsc build to signal based interruption.

Overview

When integrating typescript 7 into next.js we observed that tsc, launched as a subprocess, wouldn't exit on SIGTERM/SIGINT until the build finished. The CLI wired the signal to a context, but the compile and build paths never threaded it to the checker, so its cooperative cancellation never took effect.

This PR:

  1. Improved context threading. The context now flows through the compile and tsc -b paths (performCompilation/performIncrementalCompilationEmitFilesAndReportErrors → the build orchestrator), so the checker's existing cancellation polling takes effect.
  2. Node-compatible exit codes. On interruption the process now re-raises the signal and exits with the conventional code (130 for SIGINT, 143 for SIGTERM), matching the JS tsc. (Watch mode instead reports success — Ctrl-C is the normal way to stop a watch.)
  3. Early returns for responsiveness and consistency. Cancellation is checked at each stage of a compile so an aborted run stops promptly and never reports partial diagnostics, or reuses a canceled checker, as a complete result.
  4. Comprehensive tests. Cancellation is exercised before the compile, mid-check, and swept across every point through emit, asserting the run always aborts cleanly and never panics or reports a partial result as success.

This PR was authored by a mix of me and Claude Opus.

Known follow-up

Watch-mode rebuild cycles still run on context.Background(), so a long rebuild is only interruptible between cycles, not mid-cycle (marked with a TODO).

Alternatives

Just don't install the signal handlers, or only install them in watch mode where they can interrupt the watch loop?

Fixes microsoft/TypeScript#63856

@lukesandberg

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree [company="Vercel"]

@lukesandberg

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="Vercel"

@lukesandberg
Luke Sandberg (lukesandberg) marked this pull request as ready for review July 11, 2026 08:11
Copilot AI review requested due to automatic review settings July 11, 2026 08:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR improves tsc / tsc -b cancellation responsiveness by threading context.Context through compilation/build/emit paths so the compiler’s cooperative cancellation can take effect, and updates tsgo CLI behavior to match Node’s conventional signal exit codes. It also adds targeted regression tests to ensure cancellations abort cleanly without panics or partial results being reported.

Changes:

  • Thread context.Context through compile, emit, and build orchestrator paths and add early-return cancellation checks to avoid partial diagnostics/results.
  • Update cmd/tsgo signal handling to re-raise the interrupting signal, producing Node-compatible exit behavior on SIGINT/SIGTERM.
  • Add cancellation-focused tests for pre-canceled runs, mid-check cancellation, emit-phase cancellation, and checker-pool behavior after cancellation.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
internal/execute/watcher.go Passes a context into emit path (currently context.Background() with TODO).
internal/execute/tsctests/watcher_race_test.go Adjusts watch cancellation expectations (no watcher established; still success).
internal/execute/tsctests/tsccancel_test.go New end-to-end cancellation tests across compile/build/emit phases.
internal/execute/tsctests/runner.go Adds baseline string for new ExitStatusCanceled.
internal/execute/tsc/emit.go Plumbs context into emit/diagnostics and aborts early on cancellation.
internal/execute/tsc/compile.go Introduces ExitStatusCanceled.
internal/execute/tsc.go Threads context through compilation and incremental compilation entry points.
internal/execute/build/orchestrator.go Threads context into build orchestration; treats watch-mode cancellation as success.
internal/execute/build/buildtask.go Uses context while waiting on upstream tasks and skips expensive work when canceled.
internal/compiler/program.go Avoids re-running diagnostics / checker reuse paths once canceled.
internal/compiler/checkerpool.go Skips canceled checkers and stops feeding files once context is canceled.
internal/compiler/checkerpool_test.go New regression test ensuring global diagnostics don’t panic after mid-check cancellation.
cmd/tsgo/main.go Replaces NotifyContext with explicit signal tracking and re-raises the canceling signal.

Comment thread internal/execute/watcher.go Outdated
Comment thread internal/execute/build/orchestrator.go Outdated
Comment thread internal/execute/build/buildtask.go Outdated
Luke Sandberg (lukesandberg) added a commit to vercel/next.js that referenced this pull request Jul 13, 2026
## Follow-ups for the experimental TypeScript CLI checker

Two independent improvements to the `experimental.useTypeScriptCli`
build path.

### 1. Make `tsc` responsive to interruption

When `next build` is interrupted (Ctrl-C / `SIGTERM`), the TypeScript 7
native compiler could keep running to completion instead of stopping —
leaving a CPU-heavy process alive after the build was abandoned.

The teardown already handled termination signals and killed the whole
process group; the problem was the signal it sent. The native compiler
ignores `SIGTERM`/`SIGINT`, so the graceful signal never stopped it. We
now escalate to `SIGKILL` (Windows: `taskkill /T /F`), which reaps the
compiler on interrupt (measured ~200ms vs. running to completion).

The compiler's signal handling may be improved upstream — see
[microsoft/typescript-go#4592](microsoft/typescript-go#4592),
which threads an interruption `context` through `tsc build`. That work
is still in progress; until it lands and ships, this escalation is what
makes interruption reliable.

### 2. Skip the jest worker for the CLI checker

The type-check runs in a jest worker to isolate the TypeScript
compiler-API heap so it can be freed after checking. In CLI mode the
compiler runs in a separate `tsc` process, so there is no heap to
isolate and the worker adds nothing but an extra process and
indirection. CLI mode now runs the setup/config path in-process and
spawns `tsc` directly. The TypeScript-API checker is unchanged and still
uses the worker.

### Testing

- Unit tests for `runTypeScriptCli` (spawn options, group-SIGKILL
teardown, signal handling, listener cleanup, captured-output decoding).
- Existing `test/production/app-dir/typescript-cli` integration suite
passes (TS 6, TS 7, opt-in-required, `ignoreBuildErrors`,
`--debug-build-paths`).
- Manually verified against a TypeScript 7 project large enough to
distinguish a real kill from natural completion: the native compiler is
reaped ~200ms after interrupt.

<!-- NEXT_JS_LLM_PR -->
Luke Sandberg (lukesandberg) added a commit to vercel/next.js that referenced this pull request Jul 15, 2026
## Follow-ups for the experimental TypeScript CLI checker

Two independent improvements to the `experimental.useTypeScriptCli`
build path.

### 1. Make `tsc` responsive to interruption

When `next build` is interrupted (Ctrl-C / `SIGTERM`), the TypeScript 7
native compiler could keep running to completion instead of stopping —
leaving a CPU-heavy process alive after the build was abandoned.

The teardown already handled termination signals and killed the whole
process group; the problem was the signal it sent. The native compiler
ignores `SIGTERM`/`SIGINT`, so the graceful signal never stopped it. We
now escalate to `SIGKILL` (Windows: `taskkill /T /F`), which reaps the
compiler on interrupt (measured ~200ms vs. running to completion).

The compiler's signal handling may be improved upstream — see
[microsoft/typescript-go#4592](microsoft/typescript-go#4592),
which threads an interruption `context` through `tsc build`. That work
is still in progress; until it lands and ships, this escalation is what
makes interruption reliable.

### 2. Skip the jest worker for the CLI checker

The type-check runs in a jest worker to isolate the TypeScript
compiler-API heap so it can be freed after checking. In CLI mode the
compiler runs in a separate `tsc` process, so there is no heap to
isolate and the worker adds nothing but an extra process and
indirection. CLI mode now runs the setup/config path in-process and
spawns `tsc` directly. The TypeScript-API checker is unchanged and still
uses the worker.

### Testing

- Unit tests for `runTypeScriptCli` (spawn options, group-SIGKILL
teardown, signal handling, listener cleanup, captured-output decoding).
- Existing `test/production/app-dir/typescript-cli` integration suite
passes (TS 6, TS 7, opt-in-required, `ignoreBuildErrors`,
`--debug-build-paths`).
- Manually verified against a TypeScript 7 project large enough to
distinguish a real kill from natural completion: the native compiler is
reaped ~200ms after interrupt.

<!-- NEXT_JS_LLM_PR -->

(cherry picked from commit 63375cd)
@jakebailey

Copy link
Copy Markdown
Member

(Watch mode instead reports success — Ctrl-C is the normal way to stop a watch.)

Is this actually how it worked before? I'd think that Ctrl+C was just plain unhandled in our old compiler and therefore got a default?

Comment thread internal/execute/build/orchestrator.go Outdated
@lukesandberg

Copy link
Copy Markdown
Author

(Watch mode instead reports success — Ctrl-C is the normal way to stop a watch.)

Is this actually how it worked before? I'd think that Ctrl+C was just plain unhandled in our old compiler and therefore got a default?

i am not sure how it work in the js version, but there was a test saying that interruption reports as success in watch mode

But if that is the case, should we just do the simple thing and remove the signal handlers?

Luke Sandberg (lukesandberg) added a commit to vercel/next.js that referenced this pull request Jul 23, 2026
## Follow-ups for the experimental TypeScript CLI checker

Two independent improvements to the `experimental.useTypeScriptCli`
build path.

### 1. Make `tsc` responsive to interruption

When `next build` is interrupted (Ctrl-C / `SIGTERM`), the TypeScript 7
native compiler could keep running to completion instead of stopping —
leaving a CPU-heavy process alive after the build was abandoned.

The teardown already handled termination signals and killed the whole
process group; the problem was the signal it sent. The native compiler
ignores `SIGTERM`/`SIGINT`, so the graceful signal never stopped it. We
now escalate to `SIGKILL` (Windows: `taskkill /T /F`), which reaps the
compiler on interrupt (measured ~200ms vs. running to completion).

The compiler's signal handling may be improved upstream — see
[microsoft/typescript-go#4592](microsoft/typescript-go#4592),
which threads an interruption `context` through `tsc build`. That work
is still in progress; until it lands and ships, this escalation is what
makes interruption reliable.

### 2. Skip the jest worker for the CLI checker

The type-check runs in a jest worker to isolate the TypeScript
compiler-API heap so it can be freed after checking. In CLI mode the
compiler runs in a separate `tsc` process, so there is no heap to
isolate and the worker adds nothing but an extra process and
indirection. CLI mode now runs the setup/config path in-process and
spawns `tsc` directly. The TypeScript-API checker is unchanged and still
uses the worker.

### Testing

- Unit tests for `runTypeScriptCli` (spawn options, group-SIGKILL
teardown, signal handling, listener cleanup, captured-output decoding).
- Existing `test/production/app-dir/typescript-cli` integration suite
passes (TS 6, TS 7, opt-in-required, `ignoreBuildErrors`,
`--debug-build-paths`).
- Manually verified against a TypeScript 7 project large enough to
distinguish a real kill from natural completion: the native compiler is
reaped ~200ms after interrupt.

<!-- NEXT_JS_LLM_PR -->

(cherry picked from commit 63375cd)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Comment thread cmd/tsgo/main.go Outdated
Comment thread internal/execute/build/buildtask.go Outdated
Comment thread internal/execute/build/orchestrator.go
@jakebailey

Copy link
Copy Markdown
Member

The further this goes, the more I wonder if we should simply stop handling signals except in the LS or something. Obviously we never set up any signal handlers in tsc, right?

@lukesandberg

Copy link
Copy Markdown
Author

Yeah i more or less said that in the PR description (under Alternatives), the obvious alternative is to not install the signal handlers and just crash.

The downside of that is partial outputs (but honestly who cares)

the more interesting case is the LSP where you presumably want to be able to time out or cancel requests in response to editor actions. For that you do need to propagate context.Context objects and so the main argument for this would just be to be consistent i guess.

That being said, i know little about the LSP protocol or the requirements thereof.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

internal/execute/build/orchestrator.go:619

  • The aggregate does not always reflect cancellation. If graph generation finds a project-reference cycle, this branch runs no tasks, so a context canceled while the graph was being built still returns ProjectReferenceCycle_OutputsSkipped; a cancellation just after the last task can likewise be lost. Since runMain only re-raises the signal for ExitStatusCanceled, that interrupt is swallowed. Override the aggregate status from ctx.Err() after either branch.
		// A canceled task surfaces ExitStatusCanceled through its own report(), so the
		// aggregated status reflects cancellation without a separate override here.

Comment thread cmd/tsgo/main.go
Comment thread internal/execute/build/orchestrator.go
Masashi Kawafuji (m-kawafuji) pushed a commit to m-kawafuji/next.js that referenced this pull request Aug 8, 2026
## Follow-ups for the experimental TypeScript CLI checker

Two independent improvements to the `experimental.useTypeScriptCli`
build path.

### 1. Make `tsc` responsive to interruption

When `next build` is interrupted (Ctrl-C / `SIGTERM`), the TypeScript 7
native compiler could keep running to completion instead of stopping —
leaving a CPU-heavy process alive after the build was abandoned.

The teardown already handled termination signals and killed the whole
process group; the problem was the signal it sent. The native compiler
ignores `SIGTERM`/`SIGINT`, so the graceful signal never stopped it. We
now escalate to `SIGKILL` (Windows: `taskkill /T /F`), which reaps the
compiler on interrupt (measured ~200ms vs. running to completion).

The compiler's signal handling may be improved upstream — see
[microsoft/typescript-go#4592](microsoft/typescript-go#4592),
which threads an interruption `context` through `tsc build`. That work
is still in progress; until it lands and ships, this escalation is what
makes interruption reliable.

### 2. Skip the jest worker for the CLI checker

The type-check runs in a jest worker to isolate the TypeScript
compiler-API heap so it can be freed after checking. In CLI mode the
compiler runs in a separate `tsc` process, so there is no heap to
isolate and the worker adds nothing but an extra process and
indirection. CLI mode now runs the setup/config path in-process and
spawns `tsc` directly. The TypeScript-API checker is unchanged and still
uses the worker.

### Testing

- Unit tests for `runTypeScriptCli` (spawn options, group-SIGKILL
teardown, signal handling, listener cleanup, captured-output decoding).
- Existing `test/production/app-dir/typescript-cli` integration suite
passes (TS 6, TS 7, opt-in-required, `ignoreBuildErrors`,
`--debug-build-paths`).
- Manually verified against a TypeScript 7 project large enough to
distinguish a real kill from natural completion: the native compiler is
reaped ~200ms after interrupt.

<!-- NEXT_JS_LLM_PR -->
The CLI wires SIGINT/SIGTERM to a context in cmd/tsgo/main.go, but the
plain (non-watch, non-build) compile path never threaded it to the
checker: performCompilation/performIncrementalCompilation didn't accept a
context and EmitFilesAndReportErrors hardcoded context.Background(). As a
result a large `--noEmit` compile ignored Ctrl-C entirely — and because
signal.NotifyContext replaces the default handler, the process wouldn't
even die, it ran to completion.

Thread the context through the compile path so the checker's existing
cooperative cancellation polling (isCanceled) takes effect, and add a
distinct ExitStatusCanceled (6) returned when the compile is aborted so
incomplete diagnostics are not reported as a complete result.

Introducing cancellation into the compiler checker pool exposed a latent
panic: the pool reuses a fixed set of checkers across the diagnostics
pass, and a canceled checker panics on reuse (checkNotCanceled). Guard
the reuse sites (the second GetGlobalDiagnostics pass and the per-file
checker-group loop) with ctx.Err() so a canceled checker is not fed more
work. These guards are only necessary because the diagnostics APIs return
a bare []*ast.Diagnostic with no error channel, making an empty (canceled)
result indistinguishable from a clean one; the comments record that root
cause.

Watcher and build-task emit call sites pass context.Background() to
preserve current behavior (their cancellation is handled at the RunLoop /
orchestrator level).

Add TestTscNoEmitCancellation covering both the single-file short-circuit
and the multi-file/single-checker reuse path that would otherwise panic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Orchestrator.Start received the signal context but ran the actual
non-watch build via buildOrClean() without it — ctx only reached the
watch loop. So a plain `tsc -b` never consulted cancellation: SIGINT was
captured (default handler suppressed by signal.NotifyContext) yet the
build ran to completion. Same class of bug as the one-shot compile.

Thread ctx through Start -> buildOrClean -> rangeTask ->
buildOrCleanProject -> buildProject -> compileAndEmit ->
EmitAndReportStatistics so a long build responds to SIGINT at the
checker's granularity. rangeTask stops scheduling further projects once
canceled; buildOrClean reports ExitStatusCanceled even if cancellation
lands before any project produces a status; compileAndEmit early-returns
on a canceled result (its EmitResult is nil) without updating timestamps
or marking the project up-to-date. The canceled status dominates the
aggregate via max.

Watch mode is unchanged: the initial build and each DoCycle run to
completion with context.Background(), cancellation observed at the
RunLoop boundary. Also switch the compiler checker-group reuse guard to
also check checker.WasCanceled() (the actual reuse precondition) and add
a build-mode case to TestTscNoEmitCancellation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Trim the comments added on this branch for succinctness: keep the "no
error channel" rationale once in forEachCheckerGroupDo and cross-reference
it from the other guards, drop restated/tutorial prose. No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The rangeTask worker loop returned early on cancellation without running
the task's f, so its done/reportDone channels never closed and any in-flight
task waiting on them (waitOnUpstream / the report chain) could deadlock.

Instead always run f for every fetched task and observe cancellation inside
buildProject: skip the compile but still complete the task lifecycle. Make
waitOnUpstream and updateDownstream ctx-aware so waiters escape on cancel and
partial state is never propagated downstream. Drop the buildOrClean status
override; a canceled task surfaces ExitStatusCanceled through its own report().

Honor cancellation during a watch-mode initial build too, but report success
on that path: Ctrl-C is the expected way to exit a watch, so a non-zero code
would make normal shutdown look like a failure. One-shot tsc -b still returns
ExitStatusCanceled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses review feedback on the cancellation work:

- EmitFilesAndReportErrors: re-check ctx after Emit. Emit returns nil when
  canceled partway through (e.g. the internal no-emit-on-error recheck), which
  previously fell through to Status=Success with a nil EmitResult and crashed
  in EmitAndReportStatistics. Guard EmitResult==nil there too.

- checkerPool.GetGlobalDiagnostics: skip WasCanceled checkers. A checker
  canceled mid-check panics in checkNotCanceled when asked for global
  diagnostics; this fired from emitBuildInfo -> ensureHasErrorsForState, a path
  the earlier call-site guards missed. Fixing it in the pool covers the class,
  so the now-redundant guard in GetDiagnosticsOfAnyProgram is removed.

- Drop the speculative ctx guard in collectCheckerDiagnosticsFromFiles: it only
  ran on the project-system pool path, which tsc never exercises.

- cmd/tsgo: capture the interrupting signal (signal.Notify, not NotifyContext,
  whose context can't reveal the signal) and re-raise it on cancellation so the
  process exits with the conventional code -- 130 for SIGINT, 143 for SIGTERM --
  matching the JS tsc, instead of the internal ExitStatusCanceled (6).

- Tests: add TestTscMidCheckCancellation, which cancels *during* checking (the
  pre-canceled test cannot) and pins both the pool skip and the
  forEachCheckerGroupDo guard against checkNotCanceled panics.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up hardening after finding the call-site guards were incomplete:

- HandleNoEmitOnError: bail when already canceled. During incremental emit it is
  re-invoked per affected file and re-runs GetDiagnosticsOfAnyProgram; on a
  canceled checker that panics in checkNotCanceled (getSemanticDiagnostics).
  This vector was not covered by the earlier guards.

- Restore the ctx guard in GetDiagnosticsOfAnyProgram after getSemanticDiagnostics.
  It is independently needed for the --declaration path (no noEmitOnError, so
  HandleNoEmitOnError returns early): without it the subsequent
  GetDeclarationDiagnostics serializes types on a canceled checker and panics.

- main.go: harden the signal re-raise. Capture the signal on a dedicated channel
  (no shared variable read across the goroutine) and, after re-raising, return
  128+signum instead of blocking forever, so a failed Kill can't hang the process.

- Tighten the checkerpool cancellation comment to say why it breaks.

Tests (all with verified teeth -- each fix was reverted to confirm the test fails):
- compiler: TestGetGlobalDiagnosticsAfterCancellation directly pins the pool's
  WasCanceled skip.
- tsctests: TestTscMidCheckCancellation gains a declaration-emit case; new
  TestTscCancellationSweep cancels at every successive poll across the compile and
  into emit (incremental+noEmitOnError and declaration configs), asserting no panic
  and a consistent terminal status. This sweep is what surfaced the emit re-entry
  panic above.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The old comment claimed buildProject "reports ExitStatusCanceled and nothing
else" when this guard fires. That is wrong at the completion boundary: if the
compile finishes and cancellation trips exactly on this ctx.Err() poll,
buildProject reports Success (the work completed) -- the guard just skips the
now-pointless downstream propagation.

Rewrite it to state what the guard actually protects: updateDownstream seeds
dependents' in-memory rebuild state (never outputs), and must be skipped once
canceled so a partially-emitted program's HasChangedDtsFile can't corrupt
downstream up-to-date decisions. Note it is currently a no-op in one-shot -b
(empty downStream) and becomes load-bearing once DoCycle threads a cancelable
context into watch rebuilds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merge the pre-canceled and mid-check tests into a single table-driven
TestTscCancellationAborts: both assert the same thing (aborts with
ExitStatusCanceled, no diagnostics leaked, no checkNotCanceled panic), differing
only in when the signal fires. A per-case midCheck flag selects the cancellation
strategy via a shared runWithCancellation helper, which also folds in the timeout
guard. As a side benefit the mid-check cases now also assert no diagnostics are
reported, which the old mid-check test didn't.

TestTscCancellationSweep stays separate -- it exercises a distinct property
(no panic / no partial-success across every cancellation point) with clean source.

Teeth re-verified after the refactor: reverting each guard still fails the
corresponding case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
emit_test.go arrived on main while this branch added a ctx parameter to
EmitFilesAndReportErrors, so the merged tree failed to compile.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lukesandberg
Luke Sandberg (lukesandberg) force-pushed the lukesandberg/fix_build_cancellation branch from b08f5fe to b6e1c50 Compare August 12, 2026 16:57
@lukesandberg

Copy link
Copy Markdown
Author

Jake Bailey (@jakebailey) should I abandon this approach? Sorry i was OOO for a bit, but it would be nice to fix this issue somehow. Simply removing the signal handlers changes semantics of tsc --watch command, critically it causes ctrl-c to panic all the goroutnes, because watch is selecting on the Context.Done channel (in RunLoop so when ctrl-c kills main we end up with 'no runnable goroutines deadlock' not a great DX

So we need some solution...

  • we couild just not install the signal handlers in a non watch build, this is a bandaid imho
  • we could pursue this approach or something like it and keep the context.Context pattern

@jakebailey

Copy link
Copy Markdown
Member

I think we might just be better off not doing the signal handling at all. Or, doing it to some extent such that the API can still do cancellations, but just not feed it into the root of tsc?

I'm not 100% certain what my thoughts are yet. But I want to fix this for a patch release for sure.

@jakebailey

Copy link
Copy Markdown
Member

Yeah, so I think we should just drop the signals. I'm preparing a PR; we're trying to close up shop on this repo so I'm going to close this one just to get things moving. Thanks for looking into this, however!

@jakebailey

Copy link
Copy Markdown
Member

Made #4911.

@lukesandberg
Luke Sandberg (lukesandberg) deleted the lukesandberg/fix_build_cancellation branch August 17, 2026 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tsc is not resposive to ctrl-c

3 participants