firsthand

ADR-0004: Component identity from symbols and build ids, not Function.name

Status: accepted (Phase 1)

Problem

Components are declared as const Counter = component(fn) and used as <Counter initial={10} />. The developer must never repeat a name string, yet the framework needs a stable identity for: custom element tag names, devtools display, hot reload, and error messages. Function.name is unreliable because minifiers rewrite it.

Constraints

Options considered

  1. Function.name. Works in development, breaks under minification, and collides between packages.
  2. Runtime counter (firsthand-c17). Stable within a session, unstable across builds and across load order — unusable for SSR hydration or for a public element name.
  3. Compiler-assigned stable id. The transform assigns each component(...) call site an id derived from the package name, the module path relative to the package root and the declaration’s binding name, hashed to a short suffix. The original identifier is kept as a separate, dev-only display name.
  4. Developer-supplied string. Explicitly forbidden by the requirements.

Chosen design

Option 3, with option 2 as an explicitly degraded fallback when the compiler is absent.

// authored
export const UserCard = component(fn);
// emitted
export const UserCard = component(fn, undefined, 'acme/card/UserCard#a3f91c', 'UserCard');

Performance implications

None at runtime: identity comparisons are reference comparisons. Tag derivation happens once per component type, not per instance.

Memory implications

Two extra string references per component type (not per instance), one of which is removed in production builds.

DX implications

Rejected alternatives

Function.name (minification), runtime counters (build instability), developer-supplied strings (forbidden requirement, and a repetition the design is explicitly trying to remove).