From 4b6714dc80e50a6106c92ba45833ec9bf5be5e92 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Mon, 26 Oct 2026 12:41:22 +0800 Subject: [PATCH] Invalidate global consumers when UMD exports change Treat bound UMD global exports as affecting global scope so incremental builds invalidate unchanged consumers after namespace or type changes. Add shared CLI and watch regression histories using the existing test framework. Baselines remain to be generated: offline framework execution is blocked by uncached test dependencies. --- .../execute/incremental/programtosnapshot.go | 4 ++ tsc/internal/execute/tsctests/umd_test.go | 72 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tsc/internal/execute/tsctests/umd_test.go diff --git a/tsc/internal/execute/incremental/programtosnapshot.go b/tsc/internal/execute/incremental/programtosnapshot.go index 14f7b99fc1815..6b7d558cf971d 100644 --- a/tsc/internal/execute/incremental/programtosnapshot.go +++ b/tsc/internal/execute/incremental/programtosnapshot.go @@ -260,6 +260,10 @@ func (t *toProgramSnapshot) handlePendingCheck() { func fileAffectsGlobalScope(file *ast.SourceFile) bool { binder.BindSourceFile(file) + // UMD exports introduce global names even though the file is an external module. + if len(file.GlobalExports) > 0 { + return true + } // if file contains anything that augments to global scope we need to build them as if // they are global files as well as module if core.Some(file.ModuleAugmentations, func(augmentation *ast.ModuleName) bool { diff --git a/tsc/internal/execute/tsctests/umd_test.go b/tsc/internal/execute/tsctests/umd_test.go new file mode 100644 index 0000000000000..1c8252c142fc6 --- /dev/null +++ b/tsc/internal/execute/tsctests/umd_test.go @@ -0,0 +1,72 @@ +package tsctests + +import "testing" + +func TestTscUMD(t *testing.T) { + t.Parallel() + for _, args := range [][]string{nil, {"--watch"}} { + const umdPath = "/home/src/workspaces/project/umd.d.ts" + const present = "export as namespace UmdApi;\nexport interface Item { value: number }\n" + (&tscInput{ + subScenario: "UMD global export changes invalidate unchanged consumers", + commandLineArgs: args, + files: FileMap{ + "/home/src/workspaces/project/tsconfig.json": `{"compilerOptions":{"strict":true,"incremental":true,"noEmit":true,"types":[]},"include":["*.ts"]}`, + umdPath: present, + "/home/src/workspaces/project/client.ts": "declare const item: UmdApi.Item;\nconst checked: number = item.value;\n", + }, + edits: []*tscEdit{ + noChange, + { + caption: "remove the global namespace export", + edit: func(sys *TestSys) { + sys.writeFileNoError(umdPath, "export interface Item { value: number }\n") + }, + }, + { + caption: "restore the global namespace export", + edit: func(sys *TestSys) { + sys.writeFileNoError(umdPath, present) + }, + }, + { + caption: "rename the global namespace export", + edit: func(sys *TestSys) { + sys.replaceFileText(umdPath, "UmdApi", "OtherApi") + }, + }, + { + caption: "restore the global namespace name", + edit: func(sys *TestSys) { + sys.writeFileNoError(umdPath, present) + }, + }, + { + caption: "make the exported property incompatible", + edit: func(sys *TestSys) { + sys.replaceFileText(umdPath, "value: number", "value: string") + }, + }, + { + caption: "restore the exported property type", + edit: func(sys *TestSys) { + sys.writeFileNoError(umdPath, present) + }, + }, + { + caption: "delete the UMD declaration file", + edit: func(sys *TestSys) { + sys.removeNoError(umdPath) + }, + }, + { + caption: "restore the UMD declaration file", + edit: func(sys *TestSys) { + sys.writeFileNoError(umdPath, present) + }, + }, + noChange, + }, + }).run(t, "umd") + } +}