Skip to content

Expose diagnostic names for renderer-owned effects in @solidjs/universal #3063

Description

@birkskyum

Summary

Solid 2 diagnostics can attribute reactive updates to named computations. Custom renderers built with @solidjs/universal, however, cannot consistently name the render effects created for dynamic inserts and spreads.

This leaves an observability gap between application computations and renderer output.

Motivation

A custom renderer can observe a causal chain such as:

signal write
→ application computation
→ Universal renderer effect
→ renderer mutation/commit

Diagnostics can identify the application computation, and renderer tooling can identify the output mutation. The renderer-owned effect between them is currently anonymous, making reliable end-to-end correlation difficult.

This would be useful to custom renderers and devtools generally.

Current behavior

The Universal runtime implementations of effect and insert already accept an options argument and pass it to createRenderEffect, but this capability is not exposed by the public Renderer interface.

spread does not accept effect options and therefore cannot forward a diagnostic name to the child insertion and effects it creates internally.

Consequently, the following is not supported by the public contract:

renderer.insert(parent, accessor, marker, initial, {
  name: "counter.output"
});

renderer.spread(node, props, false, {
  name: "counter.output"
});

Proposed API

Expose a minimal renderer effect options type:

export interface RendererEffectOptions {
  /** Debug name for the renderer-owned reactive effect. */
  name?: string;
}

Add an optional trailing argument to the relevant renderer methods:

interface Renderer<NodeType> {
  effect<T>(
    fn: (prev?: T) => T,
    apply: (value: T, prev?: T) => void,
    options?: RendererEffectOptions
  ): void;

  insert<T>(
    parent: unknown,
    accessor: (() => T) | T,
    marker?: unknown | null,
    initial?: unknown,
    options?: RendererEffectOptions
  ): NodeType;

  spread<T extends object>(
    node: unknown,
    props: T,
    skipChildren?: boolean,
    options?: RendererEffectOptions
  ): void;
}

spread would forward the supplied options to its child insertion and internally created effects.

Example:

renderer.spread(node, props, false, {
  name: "counter.output"
});

With development attribution enabled, updates through that renderer output can then be identified using nodeName === "counter.output".

Compatibility

This should be backwards-compatible:

  • Every new argument is optional and trailing.
  • Existing calls retain their current behavior.
  • Calls without options have no semantic change.
  • The diagnostic name is only meaningful to development diagnostics.
  • effect and insert already support the relevant options internally; this exposes that existing capability through the public type.

Open questions

  1. Should Universal expose only name, or reuse/export a broader render-effect options type?
  2. Is one shared name for the effects created by spread sufficient, or should distinct child/ref/property names eventually be supported?
  3. Should this naming capability be considered part of Universal's supported renderer contract?

A small implementation and attribution test can be prepared against next.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions