perf: shrink bundle via listener helpers and shorter internal names#146
Draft
layershifter wants to merge 9 commits intomicrosoft:mainfrom
Draft
perf: shrink bundle via listener helpers and shorter internal names#146layershifter wants to merge 9 commits intomicrosoft:mainfrom
layershifter wants to merge 9 commits intomicrosoft:mainfrom
Conversation
Four-PR plan to shrink keyborg's emitted bundle: drop the IE11 WeakRef shim, refactor classes to closures for better minification, then measure ES2022 target and plain tsc emit as spikes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First implementation plan in the bundle-size reduction stack. Subsequent PRs (closure refactor, ES2022 spike, tsc-emit spike) will get their own plans as the stack progresses. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
.browserslistrc excludes IE 11, so the shim's fallback path was unreachable. Replace with native WeakRef, remove the companion Disposable interface, and delete the unused KeyborgCore.isDisposed() method (its body `return !!this._win` was inverted — returned true before dispose — which confirms nothing on the supported path ever called it). Bundle-size deltas on measurement fixtures: - All exports: -385 B min / -120 B gz - createKeyborg + disposeKeyborg: -385 B min / -124 B gz - KEYBORG_FOCUSIN constant: no change Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The deletion was intended in the previous refactor commit but got dropped during a stash/pop round-trip. The file is already unused (no importers) so the bundle shape is unchanged — this just removes the now-dead file from the source tree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces class KeyborgCore and class Keyborg with factory functions.
Per-instance state moves from this._x fields (which minifiers cannot
mangle at target: "es2019") to closure locals (which they can). Public
API unchanged: createKeyborg, disposeKeyborg, and the Keyborg type
(now an interface) keep the same shape. WindowWithKeyborg.__keyborg
retains its outer { core, refs } shape.
Bundle-size deltas vs post-PR-1 baseline:
- All exports: -1.693 kB min / -212 B gz
- createKeyborg + disposeKeyborg: -1.693 kB min / -211 B gz
- KEYBORG_FOCUSIN constant: no change
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
At es2022, class fields, #private syntax, optional chaining and nullish coalescing are native — no transpilation helpers. The closure refactor in microsoft#144 already eliminated the class bodies, so the remaining wins come from lib-level features. Bundle-size deltas vs post-microsoft#144 baseline: - All exports: -281 B min / -90 B gz - createKeyborg + disposeKeyborg: -242 B min / -78 B gz - KEYBORG_FOCUSIN constant: no change Decision rule (>=2% min shrink, no gz regression) satisfied: min shrinks 5.2-5.7%, gz shrinks 4.6-5.2%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Targeted follow-up to the bundle-size stack, addressing the four hottest unmangled tokens identified by token-frequency analysis of the minified output (addEventListener, removeEventListener, lastFocusedProgrammatically, __keyborgNativeFocus). - New src/dom.mts exports on() / off() helpers that wrap the capture- phase addEventListener/removeEventListener pattern. 44 call sites (22 in FocusEvent.mts, 12 in Keyborg.mts) now route through them, so the minifier only sees the long DOM method names twice (in the helper bodies) instead of 44 times. - Internal __keyborgData fields renamed: focusInHandler -> _fi, focusOutHandler -> _fo, lastFocusedProgrammatically -> _lfp, shadowTargets -> _st. Nothing outside FocusEvent.mts reads these. - The __keyborgNativeFocus flag (stored on the overridden focus function to retain the native implementation) renamed to _n. Only nativeFocus() / setupFocusEvent() / disposeFocusEvent() reference it. - Hoisted kwin.document / kwin.HTMLElement.prototype to local bindings (`doc`, `proto`) in setupFocusEvent() to let the minifier mangle repeated accesses. Bundle-size deltas vs post-microsoft#145 baseline: - All exports: -740 B min / -31 B gz - createKeyborg + disposeKeyborg: -681 B min / -31 B gz - KEYBORG_FOCUSIN constant: no change Cumulative vs main (entire stack): - All exports: -3.099 kB min (-44%) / -453 B gz (-22%) - createKeyborg + disposeKeyborg: -3.001 kB min (-44%) / -444 B gz (-22%) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📊 Bundle size reportUnchanged fixtures
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Targeted follow-up to the bundle-size stack. A token-frequency analysis of the minified output on top of #145 identified
addEventListener,removeEventListener,lastFocusedProgrammatically, and__keyborgNativeFocusas the four costliest unmangled tokens (together ~1.2 kB of the 5 kB minified bundle). This PR attacks each.src/dom.mtsexportingon(target, type, handler)/off(target, type, handler)— thin wrappers over the capture-phase DOM pattern that every keyborg listener uses. 44 call sites (22 inFocusEvent.mts, 12 inKeyborg.mts) now route through them, so the minifier only sees the long DOM method names twice.__keyborgData:focusInHandler → _fi,focusOutHandler → _fo,lastFocusedProgrammatically → _lfp,shadowTargets → _st. Nothing outsideFocusEvent.mtsreads these.__keyborgNativeFocus → _n. Stored on the overriddenfocusfunction to retain the native implementation. OnlynativeFocus()/setupFocusEvent()/disposeFocusEvent()reference it — all internal.doc/protolocals insetupFocusEvent()so repeatedkwin.document/kwin.HTMLElement.prototypeaccesses mangle.The outer
__keyborgDatakey name is preserved (any external consumer — e.g. Tabster — probing the window is untouched).Stacking
Stacked on #145 (
perf: raise build target to ES2022). Until #145 merges, this PR's diff will include #145's commit. Please review only the final commit (012219a); earlier commits are already under review in #143 / #144 / #145.Bundle size
Baseline = post-#145 tip.
createKeyborg()&disposeKeyborg()KEYBORG_FOCUSINconstantGz delta is modest because repeated tokens compress well anyway; the real win is in the raw minified size, which matters for HTTP/2 and for consumers with gzip disabled.
Cumulative vs
main(entire stack)mainmin / gzdist/index.js(raw bundle before minification): 16.36 kB → 10.45 kB (−36%).Notes for reviewers
__keyborgNativeFocusas a named marker for devtools inspection is important, we can use a longer but still mangle-friendly name (e.g._nativeFocus) — tell me and I'll adjust.event.details(plural) legacy field inKeyborgFocusInEventis still present; dropping it would be another ~30 B but is a breaking API change, so held off.Test plan
npx eslint src/ tests/cleannpm run formatcleannpm run buildsucceeds (dist/index.js12.42 kB → 10.45 kB)npm test— 9/9 Playwright specs pass locallynpm run bundle-sizeshows shrinkage on both non-trivial fixtures🤖 Generated with Claude Code