Skip to content

Warn in dev on duplicate component registration#736

Merged
titouanmathis merged 2 commits into
mainfrom
fix/registry-collision-warning
Jul 13, 2026
Merged

Warn in dev on duplicate component registration#736
titouanmathis merged 2 commits into
mainfrom
fix/registry-collision-warning

Conversation

@titouanmathis

Copy link
Copy Markdown
Contributor

addToRegistry is intentionally first-writer-wins — a name maps to a single component, mirroring customElements.define (in anticipation of future Custom Elements interop; el.__base__ is likewise keyed by config.name, so two same-name components can't coexist). But a second registration under an existing name was silently ignored, so a same-config.name subclass adding config.components had its declared children dropped from the global auto-mount registry with no signal — dynamically-injected children then never auto-mounted.

Rather than change the register-once semantics (which would diverge from the Custom Elements model), this makes the collision loud in development, like customElements.define throwing on a duplicate name:

  • Emit a dev-mode console.warn when addToRegistry is called with a name already mapped to a different constructor, steering to a unique config.name or withExtraConfig (which auto-renames).
  • Registering the same class again stays a silent no-op (idempotent).
  • No production behaviour change; register-once and utils.spec.ts "should not override an existing component" are preserved.

Added a test asserting the warn fires on a different-ctor collision and not on same-class re-registration. Full suite green (930 passed); oxlint/tsgo/prettier clean.

Context: surfaced while investigating #732. Two components sharing a config.name remains unsupported by design; this only replaces silence with a dev warning.

🤖 Generated with Claude Code

…onent

`addToRegistry` is first-writer-wins by design — a name maps to a single
component, mirroring `customElements.define` (for future interop). It silently
ignored a second registration under an existing name, dropping a same-name
subclass's declared children from the global auto-mount registry with no signal.
Keep the register-once semantics; emit a dev-mode warning on a different-ctor
collision, steering to a unique `config.name` or `withExtraConfig`.

Co-authored-by: Claude
@github-actions

Copy link
Copy Markdown

Export Size

@studiometa/js-toolkit

Name Size Diff
BASE 8.96 kB +119 B (+1.35%) 🔺
Base 8.92 kB +116 B (+1.32%) 🔺
FRAMEWORK 13.86 kB +134 B (+0.98%) 🔺
ALL 20.96 kB +102 B (+0.49%) 🔺
Unchanged

@studiometa/js-toolkit

Name Size Diff
AbstractService 528 B -
addClass 226 B -
addStyle 238 B -
animate 3.27 kB -
boundingRectToCircle 154 B -
cache 194 B -
camelCase 397 B -
clamp 67 B -
clamp01 87 B -
closestComponent 418 B -
collideCircleCircle 99 B -
collideCircleRect 159 B -
collidePointCircle 112 B -
collidePointRect 103 B -
collideRectRect 99 B -
createApp 893 B -
createEaseInOut 116 B -
createEaseOut 71 B -
createElement 592 B -
createLocalStorage 1.19 kB -
createLocalStorageProvider 264 B -
createMemoryStorageProvider 146 B -
createNoopProvider 96 B -
createRange 90 B -
createSessionStorage 1.23 kB -
createSessionStorageProvider 254 B -
createStorage 1.17 kB -
createUrlSearchParamsInHashProvider 413 B -
createUrlSearchParamsInHashStorage 1.21 kB -
createUrlSearchParamsProvider 386 B -
createUrlSearchParamsStorage 1.21 kB -
damp 78 B -
dashCase 372 B -
debounce 92 B -
DECORATORS 7.59 kB -
defineFeatures 322 B -
domScheduler 296 B -
DragService 1.94 kB -
ease 435 B -
easeInCirc 68 B -
easeInCubic 59 B -
easeInExpo 80 B -
easeInOutCirc 141 B -
easeInOutCubic 130 B -
easeInOutExpo 134 B -
easeInOutQuad 128 B -
easeInOutQuart 133 B -
easeInOutQuint 152 B -
easeInOutSine 151 B -
easeInQuad 63 B -
easeInQuart 61 B -
easeInQuint 62 B -
easeInSine 77 B -
easeLinear 49 B -
easeOutCirc 115 B -
easeOutCubic 103 B -
easeOutExpo 112 B -
easeOutQuad 103 B -
easeOutQuart 100 B -
easeOutQuint 103 B -
easeOutSine 121 B -
endsWith 88 B -
getAncestorWhere 91 B -
getAncestorWhereUntil 119 B -
getClosestParent 182 B -
getComponentResolver 138 B -
getDirectChildren 201 B -
getInstanceFromElement 92 B -
getInstances 185 B -
getOffsetSizes 159 B -
hasWindow 62 B -
HELPERS 2.54 kB -
historyPush 499 B -
historyReplace 503 B -
importOnInteraction 887 B -
importOnMediaQuery 236 B -
importWhenIdle 223 B -
importWhenPrefersMotion 275 B -
importWhenVisible 912 B -
inertiaFinalValue 142 B -
isArray 70 B -
isBoolean 78 B -
isDefined 86 B -
isDev 72 B -
isDirectChild 219 B -
isEmpty 207 B -
isEmptyString 93 B -
isFunction 72 B -
isNull 72 B -
isNumber 84 B -
isObject 105 B -
isString 86 B -
keyCodes 97 B -
KeyService 854 B -
lerp 57 B -
loadElement 169 B -
loadIframe 189 B -
loadImage 188 B -
loadLink 186 B -
loadScript 197 B -
LoadService 593 B -
localStorageProvider 715 B -
logTree 510 B -
lowerCase 60 B -
map 71 B -
matrix 106 B -
mean 91 B -
memo 100 B -
memoize 189 B -
memoryStorageProvider 607 B -
MutationService 799 B -
nextFrame 162 B -
nextMicrotask 111 B -
nextTick 134 B -
noop 39 B -
noopValue 49 B -
objectToURLSearchParams 302 B -
pascalCase 374 B -
PointerService 1.07 kB -
queryComponent 613 B -
queryComponentAll 616 B -
Queue 226 B -
RafService 956 B -
random 64 B -
randomInt 77 B -
randomItem 211 B -
registerComponent 103 B -
removeClass 222 B -
removeStyle 238 B -
ResizeService 1.01 kB -
round 56 B -
saveActiveElement 56 B -
ScrollService 1.28 kB -
scrollTo 2.31 kB -
SERVICES 4.01 kB -
sessionStorageProvider 712 B -
SmartQueue 411 B -
smoothTo 473 B -
snakeCase 374 B -
spring 115 B -
startsWith 87 B -
throttle 101 B -
toggleClass 225 B -
transform 288 B -
transition 916 B -
trapFocus 363 B -
tween 1.72 kB -
untrapFocus 45 B -
upperCase 54 B -
urlSearchParamsInHashProvider 537 B -
urlSearchParamsProvider 539 B -
useDrag 1.98 kB -
useKey 871 B -
useLoad 610 B -
useMutation 829 B -
usePointer 1.09 kB -
useRaf 963 B -
useResize 1.02 kB -
useScheduler 290 B -
useScroll 1.29 kB -
UTILS 9.59 kB -
wait 79 B -
withBreakpointManager 1.42 kB -
withBreakpointObserver 1.61 kB -
withDrag 2.09 kB -
withExtraConfig 135 B -
withFreezedOptions 140 B -
withGroup 252 B -
withIntersectionObserver 260 B -
withLeadingCharacters 88 B -
withLeadingSlash 107 B -
withMountOnMediaQuery 322 B -
withMountWhenInView 286 B -
withMountWhenPrefersMotion 355 B -
withMutation 959 B -
withName 81 B -
withoutLeadingCharacters 86 B -
withoutLeadingCharactersRecursive 124 B -
withoutLeadingSlash 93 B -
withoutTrailingCharacters 98 B -
withoutTrailingCharactersRecursive 129 B -
withoutTrailingSlash 103 B -
withRelativePointer 1.23 kB -
withResponsiveOptions 2.26 kB -
withScrolledInView 2.93 kB -
withTrailingCharacters 96 B -
withTrailingSlash 120 B -
wrap 77 B -

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.45%. Comparing base (1e38f4d) to head (7b76581).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #736   +/-   ##
=======================================
  Coverage   97.45%   97.45%           
=======================================
  Files         162      162           
  Lines        3735     3737    +2     
  Branches     1043     1044    +1     
=======================================
+ Hits         3640     3642    +2     
  Misses         85       85           
  Partials       10       10           
Flag Coverage Δ
eslint-plugin-js-toolkit 94.18% <ø> (-3.27%) ⬇️
js-toolkit 97.45% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq

codspeed-hq Bot commented Jul 13, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 127 untouched benchmarks


Comparing fix/registry-collision-warning (7b76581) with main (1e38f4d)

Open in CodSpeed

@titouanmathis titouanmathis merged commit 1509e11 into main Jul 13, 2026
10 checks passed
@titouanmathis titouanmathis deleted the fix/registry-collision-warning branch July 13, 2026 16:26
@titouanmathis titouanmathis mentioned this pull request Jul 13, 2026
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.

1 participant