From 21f4cb5cc6a7e184c02cd388a694db259906a63f Mon Sep 17 00:00:00 2001 From: Titouan Mathis Date: Mon, 13 Jul 2026 18:12:35 +0200 Subject: [PATCH 1/2] Warn in dev when a component name is registered with a different component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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 --- packages/js-toolkit/Base/utils.ts | 10 +++++++++- packages/tests/Base/utils.spec.ts | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/js-toolkit/Base/utils.ts b/packages/js-toolkit/Base/utils.ts index 8d69bb0f9..642647568 100644 --- a/packages/js-toolkit/Base/utils.ts +++ b/packages/js-toolkit/Base/utils.ts @@ -1,4 +1,4 @@ -import { isArray, isDefined, SmartQueue, dashCase } from '../utils/index.js'; +import { isArray, isDefined, SmartQueue, dashCase, isDev } from '../utils/index.js'; import type { Base, BaseConfig, BaseConstructor } from './index.js'; import { features } from './features.js'; import { useMutation } from '../services/MutationService.js'; @@ -202,6 +202,14 @@ function mutationCallback() { export function addToRegistry(nameOrSelector: string, ctor: BaseConstructor) { if (registry().has(nameOrSelector)) { + if (isDev && registry().get(nameOrSelector) !== ctor) { + console.warn( + `[${nameOrSelector}] A different component is already registered under this name. ` + + 'Registration is ignored — like `customElements.define`, a name maps to a single ' + + 'component. Give this component a unique `config.name` (or use `withExtraConfig`, ' + + 'which renames automatically).', + ); + } return; } diff --git a/packages/tests/Base/utils.spec.ts b/packages/tests/Base/utils.spec.ts index 5c1384de4..63f95bff8 100644 --- a/packages/tests/Base/utils.spec.ts +++ b/packages/tests/Base/utils.spec.ts @@ -146,6 +146,25 @@ describe('The Base utils', () => { await Promise.all(Array.from(fooInstances).map((i) => i.$destroy())); }); + it('should warn in dev mode when a name is registered with a different component', () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); + const Foo = withName(Base, 'Foo'); + const Bar = withName(Base, 'Bar'); + + addToRegistry('CollisionName', Foo); + // Re-registering the SAME class is idempotent and must not warn. + addToRegistry('CollisionName', Foo); + expect(warn).not.toHaveBeenCalled(); + + // A DIFFERENT class under the same name stays register-once (ignored) but warns. + addToRegistry('CollisionName', Bar); + expect(warn).toHaveBeenCalledTimes(1); + expect(warn.mock.calls[0][0]).toContain('CollisionName'); + expect(warn.mock.calls[0][0]).toContain('customElements.define'); + + warn.mockRestore(); + }); + it('should register new components', async () => { const TestComponent = withName(Base, 'TestComponent'); const registryKey = '__JS_TOOLKIT_REGISTRY__'; From 7b76581a503446df28efbbbb0a8710df6bb73e26 Mon Sep 17 00:00:00 2001 From: Titouan Mathis Date: Mon, 13 Jul 2026 18:13:11 +0200 Subject: [PATCH 2/2] Update changelog for duplicate-registration dev warning Co-authored-by: Claude --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6346bf67c..7037c36ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format ## [Unreleased] +### Changed + +- Warn in development when a component is registered under a name already used by a different component, instead of silently ignoring it — registration stays first-writer-wins, mirroring `customElements.define` ([#736](https://github.com/studiometa/js-toolkit/pull/736)) + ## [v3.6.1](https://github.com/studiometa/js-toolkit/compare/3.6.0..3.6.1) (2026-07-13) ### Fixed