Patch Changes
-
b298154: Move the runtimes' seams off the
solid-jssurface and behindsolid-js/internalmerge()/omit()returning lazy views (#3454) gavespread()and
ssrElement()a protocol for reading props leaf by leaf instead of trapping
through the proxy per key. Because@solidjs/weband@solidjs/universal
depend onsolid-jsalone — never on@solidjs/signalsdirectly, so an app
holds exactly one reactive engine — every piece of that protocol went out
throughsolid-js's main export: eleven names,mergeSourcesbefore them in
#3325, on the public surface with no marking. None of it is API.The protocol (
viewOf,mergeView/omitView,MergeView/OmitView,
sourceKeys/sourceHas/sourceGet,hasStaticKeys,resolvedTable, the
SOURCE_*kinds,SourceKind) now lives on asolid-js/internalsubpath,
along with the server-scope seams that were already@internalin JSDoc and
consumed only by@solidjs/web(ssrHandleError,ssrScope,
runInServerComponentScope,inServerComponentScope,creationStamp,
getProjectionTrace,materializeContainerTrace). The names stay exported
from the main entries at runtime, so the subpath shares one module state and
the single-engine guarantee is untouched;stripInternalkeeps them out of
the generated declarations, so TypeScript no longer offers or types them.
internal-surface.spec.tspins the boundary in both directions.Also dropped from the entries:
storeIsShallow,storeHasFamily,
storeHasOptimisticFamily(leftovers of the gutted patch channel),
storePathand$REFRESH(referenced only by@solidjs/signals's own
internals), andNoHydrateContext(@internal, used only bysolid-js's
server code). Nothing in the repo consumed them.No runtime behavior change.
merge,omit, and the rest of the reactive
surface are unaffected. -
899c2c4:
merge()andomit()are always lazy views, and props consumers read their leavesomit(props, ...keys)returns a live view ofpropsfor every input — a plain object included — instead of copying it with agetOwnPropertyDescriptor+definePropertyper prop. A predicate form hides keys by rule without enumerating first:omit(props, k => k[0] === "$").merge()no longer builds an eager copy when its sources are plain objects: underProxyit always returns an O(1) view over the flattened sources (a single non-function source is returned as is).The two compose flat. An
omit()over amerge()carries one filtered view per flattened merge source, amerge()over anomit()takes the view record as a leaf, and nested omits fold their filters into one record. A component chain ofmerge(defaults) → omit(consumed) → merge(statics) → omit("as")— the shape headless-UI libraries render every element through — collapses to leaf views over the original objects, each with its accumulated filter, with no proxy layer left between the outermost spread and the author's props.merge()keeps the omitted keys hidden by construction (#3014) rather than by treating the omit as opaque. Construction cost drops 3–7× at depth 1–7; the SSR polymorphic-chain bench (#3448) runs ~2.4× faster.Reads stay cheap: a view over plain objects resolves a key → owning-leaf table once, on first read, and every
get/has/descriptor is one lookup after that.spread()(DOM and universal) andssrElement()read the leaves directly — never through the proxies' traps — and walk that table when there is one, so an effect rerun costs one read per key, as it did over the copy. Both proxies use a class target and one shared handler (no per-instance closures).A view over a store asks the store nothing but the read. Each source's kind (plain object, omit record, proxy, memo) is decided once, when the view is built, and carried beside it — every brand check on a Proxy is a trap (
instanceofis agetPrototypeOftrap, as expensive as a store read), and store detection goes through$TARGET, a symbol the store'sgettrap answers on its fast path, never its generic tracked-read path.merge(defaults, store)constructs ~30% faster than the copy did and reads ~15% faster;omit(store)reads at parity.The views tell the truth:
Object.getOwnPropertyDescriptor(view, key)reports a data descriptor only when the key is a data property of a plain leaf (the compiler's encoding of a static prop) and an accessor for a getter, a store key, or a memo source. Together with the new internalhasStaticKeys(),spread()now skips the children effect for static children behindomit/mergelayers (#3388 through views).Behavior changes:
- Writes to a
merge()oromit()result are no-ops (they already were for the proxy forms). A caller that needs its own object copies it ({ ...merged }), and the copy carries no sources (#3384).@solidjs/htmlnow collects its own props and spreads into onemerge()at the end instead of assigning onto the result. - A data property on a source is read live through the view rather than snapshotted at
merge()/omit()time. - Key order of a merged view is the merged order — every key at the position of the last source that carries it — matching
ssrElement's array form. - Sources are treated as own-keyed; a key added to a plain source after merging is not seen (the copy did not see it either).
- Enumerating a view through its traps (
for…in,Object.keys,{ ...view }) costs a trap per key, as any proxy does; the internal consumers avoid it. Environments withoutProxykeep the copy paths.
Internal helpers for consumers, exported from
solid-js:viewOf(o),mergeView(o),omitView(o),sourceKeys(entry, kind),sourceHas(entry, kind, key),sourceGet(entry, kind, key),hasStaticKeys(o),resolvedTable(o), theSOURCE_*kinds. - Writes to a
-
1643d2a: The universal renderer's
spread()follows the@solidjs/webcontract (#3388):reffolds into the props effect and is re-applied only when its identity changes (refs run with no owner, so nothing they create is disposed by the fold); children keep their own ownedinsert— that effect owns the child subtree — but a plain object whosechildrenis a data property inserts the value with no effect at all. Three reactive nodes become two when children flow through the spread, one when they don't.spreadalso resolves a lone function source inside its own tracking scopes and accepts an array of sources —spread(node, [a, b], skipChildren)— the union of their keys with later sources winning, only the winning source read, function sources called inline with no merge and no memo. Both compilers' universal output uses it: a lone spread passes straight through (reactive included, no moremergeProps(() => …)), and several sources compile to the array instead of amergeProps()call. -
Updated dependencies [8bf04ea]
-
Updated dependencies [d826cd3]
-
Updated dependencies [cc0396b]
-
Updated dependencies [53280e7]
-
Updated dependencies [3154ed6]
-
Updated dependencies [b298154]
-
Updated dependencies [899c2c4]
-
Updated dependencies [3ae9e92]
-
Updated dependencies [328580f]
-
Updated dependencies [61a114c]
-
Updated dependencies [0d8347a]
-
Updated dependencies [7623ce1]
-
Updated dependencies [56858e7]
-
Updated dependencies [af94f67]
-
Updated dependencies [e87d694]
-
Updated dependencies [5426ffb]
-
Updated dependencies [63560a1]
-
Updated dependencies [34287d8]
- solid-js@2.0.0-rc.9