Skip to content

Commit 59dd11f

Browse files
committed
docs: AI-readiness JSDoc handoff prep
Backfill JSDoc examples on previously-undocumented public APIs flagged in the v2 docs categorization audit (`getObserver`, `isDisposed`, `createRenderEffect`, `onCleanup`, `createErrorBoundary`, `createLoadingBoundary`, `createRevealOrder`, `flatten`, `enableExternalSource`, `NotReadyError`, `NoHydration`, `Hydration`, `isServer`, `isDev`). Normalize inline JSDoc code fences to `@example` tags on the JSX components (`<For>`, `<Repeat>`, `<Switch>`, `<Errored>`, `<Reveal>`, `dynamic`, `<Dynamic>`) so the doc generator can extract examples uniformly. Tag cross-package wiring and compiler-emitted exports with `@internal` so the doc generator can hide them from the user-facing surface (`getContext`, `setContext`, `createOwner`, `getNextChildId` / `peekNextChildId`, `enforceLoadingBoundary`, the store / refresh brand symbols, `\$DEVCOMP`, `sharedConfig`, `enableHydration`, `NoHydrateContext`, the `ssr*` helpers in `server-mock`, `escape`, `resolveSSRNode`, `mergeProps`, `ssrHandleError` / `ssrRunInScope`). Mention `isEqual` as the default on the `equals` field of `SignalOptions` and `MemoOptions` so the equals comparator has a docs landing spot. Made-with: Cursor
1 parent 7536f66 commit 59dd11f

18 files changed

Lines changed: 469 additions & 48 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"solid-js": patch
3+
"@solidjs/web": patch
4+
"@solidjs/signals": patch
5+
---
6+
7+
Docs prep for the 2.0 reference auto-generation pass: backfill JSDoc examples on previously-undocumented public APIs (`getObserver`, `isDisposed`, `createRenderEffect`, `onCleanup`, `createErrorBoundary`, `createLoadingBoundary`, `createRevealOrder`, `flatten`, `enableExternalSource`, `NotReadyError`, `NoHydration`, `Hydration`, `isServer`, `isDev`); normalize inline JSDoc code fences to `@example` tags on the JSX components (`<For>`, `<Repeat>`, `<Switch>`, `<Errored>`, `<Reveal>`, `dynamic`, `<Dynamic>`); and tag cross-package wiring / compiler-emitted exports with `@internal` so the doc generator can hide them from the user-facing surface (`getContext`, `setContext`, `createOwner`, `getNextChildId`, `peekNextChildId`, `enforceLoadingBoundary`, `sharedConfig`, `enableHydration`, `NoHydrateContext`, `$DEVCOMP`, `$PROXY`, `$REFRESH`, `$TRACK`, `$TARGET`, `$DELETED`, `ssr*` helpers, `escape`, `resolveSSRNode`, `mergeProps`, `ssrHandleError`, `ssrRunInScope`). Also extends the `equals` field JSDoc on `SignalOptions` / `MemoOptions` to mention `isEqual` as the default.

packages/solid-signals/src/boundaries.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,17 @@ function createCollectionBoundary<T>(
407407
* @param fallback the fallback shown while async reads in `fn` are unresolved
408408
* @param options `on` — accessor whose value scopes the boundary; when set,
409409
* transitions caused by writes to other reactive sources are *not* caught
410+
*
411+
* @example
412+
* ```tsx
413+
* // Custom boundary component built on top of the primitive.
414+
* function MyLoading(props: { fallback: JSX.Element; children: JSX.Element }) {
415+
* return createLoadingBoundary(
416+
* () => props.children,
417+
* () => props.fallback
418+
* ) as unknown as JSX.Element;
419+
* }
420+
* ```
410421
*/
411422
export function createLoadingBoundary(
412423
fn: () => any,
@@ -424,6 +435,20 @@ export function createLoadingBoundary(
424435
*
425436
* App code should use `<Errored fallback={...}>` instead — reach for this only
426437
* when authoring custom boundary components.
438+
*
439+
* @example
440+
* ```tsx
441+
* // Custom boundary that wraps the primitive and adds telemetry.
442+
* function TracedErrored(props: { fallback: (e: unknown) => JSX.Element; children: JSX.Element }) {
443+
* return createErrorBoundary(
444+
* () => props.children,
445+
* (err, reset) => {
446+
* reportError(err);
447+
* return props.fallback(err);
448+
* }
449+
* ) as unknown as JSX.Element;
450+
* }
451+
* ```
427452
*/
428453
export function createErrorBoundary<U>(
429454
fn: () => any,
@@ -469,6 +494,17 @@ export function createErrorBoundary<U>(
469494
* - `together` — every direct slot is minimally ready.
470495
* - `natural` — any direct slot has visible content (leaves on resolve; nested
471496
* composites when fully ready, since natural treats composites as atomic).
497+
*
498+
* @example
499+
* ```ts
500+
* // Primitive form of `<Reveal>` — coordinate sibling loading boundaries
501+
* // programmatically. App code uses the JSX `<Reveal>` component instead.
502+
* // Both options are accessors so they can react to state changes.
503+
* createRevealOrder(
504+
* () => renderSiblings(),
505+
* { order: () => mode(), collapsed: () => true }
506+
* );
507+
* ```
472508
*/
473509
export function createRevealOrder<T>(
474510
fn: () => T,
@@ -510,6 +546,15 @@ export function createRevealOrder<T>(
510546
* @param options
511547
* - `skipNonRendered` — drop values that won't render
512548
* - `doNotUnwrap` — leave function children as-is (caller will resolve)
549+
*
550+
* @example
551+
* ```ts
552+
* // Custom renderer walking a children tree manually. Most authors should
553+
* // use `children()` from solid-js, which memoizes the resolved value.
554+
* function renderChildren(value: unknown): unknown {
555+
* return flatten(value, { skipNonRendered: true });
556+
* }
557+
* ```
513558
*/
514559
export function flatten(
515560
children: any,

packages/solid-signals/src/core/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ export const SUPPORTS_PROXY = typeof Proxy === "function";
3636

3737
export const defaultContext = {};
3838

39+
/**
40+
* Brand symbol used by `Refreshable<T>` values (projection stores, async
41+
* memos) to expose their underlying computation to `refresh()`. Not part of
42+
* the user-facing API.
43+
*
44+
* @internal
45+
*/
3946
export const $REFRESH = Symbol("refresh");
4047

4148
/**

packages/solid-signals/src/core/context.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@ export function createContext<T>(defaultValue?: T, description?: string): Contex
2222
}
2323

2424
/**
25-
* Attempts to get a context value for the given key.
25+
* Low-level owner-targeted context read. The user-facing read API is
26+
* `useContext` (in `solid-js`), which wraps this primitive. Exposed here for
27+
* cross-package wiring (e.g. hydration-aware context plumbing).
2628
*
2729
* @throws `NoOwnerError` if there's no owner at the time of call.
2830
* @throws `ContextNotFoundError` if a context value has not been set yet.
31+
*
32+
* @internal
2933
*/
3034
export function getContext<T>(context: Context<T>, owner: Owner | null = getOwner()): T {
3135
if (!owner) {
@@ -44,9 +48,13 @@ export function getContext<T>(context: Context<T>, owner: Owner | null = getOwne
4448
}
4549

4650
/**
47-
* Attempts to set a context value on the parent scope with the given key.
51+
* Low-level owner-targeted context write. The user-facing API is
52+
* `createContext` (in `solid-js`); its provider component wraps this
53+
* primitive. Exposed here for cross-package wiring.
4854
*
4955
* @throws `NoOwnerError` if there's no owner at the time of call.
56+
*
57+
* @internal
5058
*/
5159
export function setContext<T>(context: Context<T>, value?: T, owner: Owner | null = getOwner()) {
5260
if (!owner) {

packages/solid-signals/src/core/error.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
/**
2+
* Thrown by a tracked read whose value is currently pending (an async memo /
3+
* `createSignal(asyncFn)` / projection / store derivation that hasn't settled
4+
* yet). Surfacing through the reactive graph is what suspends the consumer
5+
* scope — the nearest enclosing `<Loading>` boundary catches the throw and
6+
* renders its fallback until the source resolves.
7+
*
8+
* App code rarely catches this directly; `<Loading>` is the canonical
9+
* handler. The error type is exposed for advanced cases — e.g. interop layers
10+
* that bridge Solid's pending-throw protocol to a different async strategy,
11+
* or tests that want to assert on the suspension shape.
12+
*
13+
* @example
14+
* ```ts
15+
* // Advanced: distinguish "not ready yet" from a real error in custom
16+
* // boundary plumbing. App code should rely on `<Loading>` / `<Errored>`.
17+
* try {
18+
* const value = readReactiveSource();
19+
* } catch (err) {
20+
* if (err instanceof NotReadyError) throw err; // re-throw to suspend
21+
* reportError(err);
22+
* }
23+
* ```
24+
*/
125
export class NotReadyError extends Error {
226
constructor(public source: any) {
327
super();

packages/solid-signals/src/core/external.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@ export let externalSourceConfig: {
2525
* @param config.factory receives `(fn, trigger)` — wrap fn execution in external tracking,
2626
* call trigger when external deps change. Return `{ track, dispose }`.
2727
* @param config.untrack optional wrapper for `untrack` — disables external tracking too.
28+
*
29+
* @example
30+
* ```ts
31+
* // Bridge an external "subscribe / notify" library into Solid's graph.
32+
* // `factory` wraps every Solid compute so the external library can attach
33+
* // its own dependency tracker; `trigger` re-runs the compute on external
34+
* // change. `untrack` mirrors Solid's `untrack()` into the external library
35+
* // so that reads inside `untrack(...)` don't get tracked twice.
36+
* enableExternalSource({
37+
* factory: (compute, trigger) => {
38+
* const sub = externalLib.subscribe(trigger);
39+
* return {
40+
* track: prev => externalLib.run(() => compute(prev)),
41+
* dispose: () => sub.unsubscribe()
42+
* };
43+
* },
44+
* untrack: fn => externalLib.untracked(fn)
45+
* });
46+
* ```
2847
*/
2948
export function enableExternalSource(config: ExternalSourceConfig): void {
3049
const { factory, untrack: untrackFn = fn => fn() } = config;

packages/solid-signals/src/core/owner.ts

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,22 @@ function childId(owner: Owner, consume: boolean): string {
9898
throw new Error("Cannot get child id from owner without an id");
9999
}
100100

101+
/**
102+
* Allocates and returns the next stable child id for `owner`. Used by
103+
* hydration plumbing and `createUniqueId`. Not part of the user-facing API.
104+
*
105+
* @internal
106+
*/
101107
export function getNextChildId(owner: Owner): string {
102108
return childId(owner, true);
103109
}
104110

111+
/**
112+
* Returns the *next* child id for `owner` without consuming it. Used by
113+
* hydration plumbing to peek at the id a future child will receive.
114+
*
115+
* @internal
116+
*/
105117
export function peekNextChildId(owner: Owner): string {
106118
return childId(owner, false);
107119
}
@@ -118,6 +130,15 @@ function formatId(prefix: string, id: number) {
118130
* Used by reactive primitives that need to know whether they're inside a
119131
* tracking scope. App code rarely needs this — see `getOwner()` for the
120132
* lifecycle owner instead.
133+
*
134+
* @example
135+
* ```ts
136+
* // Library predicate: only register a hot-path subscription when the
137+
* // caller is inside a tracking scope (memo / effect compute / JSX).
138+
* function trackIfTracked(source: () => unknown) {
139+
* if (getObserver()) source();
140+
* }
141+
* ```
121142
*/
122143
export function getObserver(): Owner | null {
123144
if (pendingCheckActive || latestReadActive) return PENDING_OWNER;
@@ -158,15 +179,33 @@ export function cleanup(fn: Disposable): Disposable {
158179
return fn;
159180
}
160181

161-
/** Returns `true` if the owner has been disposed (or marked zombie pending disposal). */
182+
/**
183+
* Returns `true` if the owner has been disposed (or marked zombie pending
184+
* disposal). Pair with a captured owner to bail out of late callbacks whose
185+
* surrounding component already unmounted.
186+
*
187+
* @example
188+
* ```ts
189+
* function onSettleSafe(fn: () => void) {
190+
* const owner = getOwner();
191+
* queueMicrotask(() => {
192+
* if (owner && isDisposed(owner)) return; // component unmounted; skip
193+
* runWithOwner(owner, fn);
194+
* });
195+
* }
196+
* ```
197+
*/
162198
export function isDisposed(node: Owner): boolean {
163199
return !!((node as any)._flags & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE));
164200
}
165201

166202
/**
167203
* Creates a fresh owner attached as a child of the current owner (or as a
168-
* detached root if there is none). Mostly used by framework internals to
169-
* group cleanups; app code should prefer `createRoot()` or `runWithOwner()`.
204+
* detached root if there is none). Used by framework internals to group
205+
* cleanups; app code should use `createRoot()` (host a reactive scope outside
206+
* a component) or `runWithOwner()` (re-enter a captured owner).
207+
*
208+
* @internal
170209
*/
171210
export function createOwner(options?: { id?: string; transparent?: boolean }) {
172211
const parent = context;

packages/solid-signals/src/core/scheduler.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ function sweepTransientStoreNodes(): void {
8080
export function resetUnhandledAsync(): void {
8181
_hitUnhandledAsync = false;
8282
}
83+
/**
84+
* Toggles the dev-mode "must be inside a `<Loading>` boundary" enforcement
85+
* window. Only `render()` calls this — wrapping the initial mount so that a
86+
* top-level uncaught async read surfaces the diagnostic. Not part of the
87+
* user-facing API.
88+
*
89+
* @internal
90+
*/
8391
export function enforceLoadingBoundary(enabled: boolean): void {
8492
_enforceLoadingBoundary = enabled;
8593
}

packages/solid-signals/src/signals.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ import { globalQueue } from "./core/scheduler.js";
4949
*
5050
* Cannot be used inside `createTrackedEffect` or `onSettled` — return a
5151
* cleanup function from the callback body instead.
52+
*
53+
* @example
54+
* ```ts
55+
* // Library shape: thread a resource's disposal into a *captured* owner
56+
* // from a factory that has no settle-phase setup of its own. `onSettled`
57+
* // would queue a callback we don't need; `onCleanup` is the leaner
58+
* // primitive when the only job is "register disposal on this owner".
59+
* function bindToOwner<T extends { dispose(): void }>(owner: Owner, resource: T): T {
60+
* runWithOwner(owner, () => onCleanup(() => resource.dispose()));
61+
* return resource;
62+
* }
63+
* ```
5264
*/
5365
export function onCleanup(fn: Disposable): Disposable {
5466
if (__DEV__) {
@@ -151,7 +163,12 @@ export interface EffectOptions extends BaseEffectOptions {
151163
export interface SignalOptions<T> {
152164
/** Debug name (dev mode only) */
153165
name?: string;
154-
/** Custom equality function, or `false` to always notify subscribers */
166+
/**
167+
* Custom equality function, or `false` to always notify subscribers.
168+
* Defaults to reference equality (`isEqual`). Pass a comparator (e.g.
169+
* `(a, b) => a.id === b.id`) for value-based equality, or `false` to
170+
* notify on every write regardless of equality.
171+
*/
155172
equals?: false | ((prev: T, next: T) => boolean);
156173
/** Suppress dev-mode warnings when writing inside an owned scope */
157174
ownedWrite?: boolean;
@@ -171,7 +188,12 @@ export interface MemoOptions<T> {
171188
name?: string;
172189
/** When true, the owner is invisible to the ID scheme -- inherits parent ID and doesn't consume a childCount slot */
173190
transparent?: boolean;
174-
/** Custom equality function, or `false` to always notify subscribers */
191+
/**
192+
* Custom equality function, or `false` to always notify subscribers.
193+
* Defaults to reference equality (`isEqual`). Pass a comparator (e.g.
194+
* `(a, b) => a.id === b.id`) for value-based equality, or `false` to
195+
* notify on every recompute regardless of equality.
196+
*/
175197
equals?: false | ((prev: T, next: T) => boolean);
176198
/** Callback invoked when the computed loses all subscribers */
177199
unobserved?: () => void;
@@ -385,13 +407,29 @@ export function createEffect<T>(
385407
* Creates a reactive computation that runs during the render phase as DOM elements
386408
* are created and updated but not necessarily connected.
387409
*
410+
* Same compute / effect split as `createEffect`, but scheduled inside the render
411+
* queue rather than after it. Reach for this only when authoring renderer
412+
* plumbing (custom DOM bindings, JSX-generated `insert()` / `spread()` calls).
413+
* App code should use `createEffect`.
414+
*
388415
* ```typescript
389416
* createRenderEffect<T>(compute, effectFn, options?: EffectOptions);
390417
* ```
391418
* @param compute a function that receives its previous value and returns a new value used to react on a computation
392419
* @param effectFn a function that receives the new value and is used to perform side effects
393420
* @param options `EffectOptions` -- name, defer, schedule
394421
*
422+
* @example
423+
* ```ts
424+
* // Custom directive: bind an element's textContent to a reactive source.
425+
* function bindText(el: HTMLElement, source: () => string) {
426+
* createRenderEffect(
427+
* () => source(),
428+
* value => { el.textContent = value; }
429+
* );
430+
* }
431+
* ```
432+
*
395433
* @description https://docs.solidjs.com/reference/secondary-primitives/create-render-effect
396434
*/
397435
export function createRenderEffect<T>(

packages/solid-signals/src/store/store.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ export type NoFn<T> = T extends Function ? never : T;
5858
type DataNode = Signal<any>;
5959
type DataNodes = Record<PropertyKey, DataNode>;
6060

61+
/**
62+
* Brand symbols used internally by the store proxy / projection plumbing.
63+
* Cross-package wiring; not part of the user-facing API.
64+
*
65+
* @internal
66+
*/
6167
export const $TRACK = Symbol(__DEV__ ? "STORE_TRACK" : 0),
6268
$TARGET = Symbol(__DEV__ ? "STORE_TARGET" : 0),
6369
$PROXY = Symbol(__DEV__ ? "STORE_PROXY" : 0),

0 commit comments

Comments
 (0)