import { untrack, createSignal, createResource, createMemo, devComponent, $PROXY, $DEVCOMP } from "../reactive/signal.js"; import { sharedConfig, nextHydrateContext, setHydrateContext } from "./hydration.js"; import type { JSX } from "../jsx.js"; let hydrationEnabled = false; export function enableHydration() { hydrationEnabled = true; } /** * A general `Component` has no implicit `children` prop. If desired, you can * specify one as in `Component<{name: String, children: JSX.Element>}`. */ export type Component

= (props: P) => JSX.Element; /** * Extend props to forbid the `children` prop. * Use this to prevent accidentally passing `children` to components that * would silently throw them away. */ export type VoidProps

= P & { children?: never }; /** * `VoidComponent` forbids the `children` prop. * Use this to prevent accidentally passing `children` to components that * would silently throw them away. */ export type VoidComponent

= Component>; /** * Extend props to allow an optional `children` prop with the usual * type in JSX, `JSX.Element` (which allows elements, arrays, functions, etc.). * Use this for components that you want to accept children. */ export type ParentProps

= P & { children?: JSX.Element }; /** * `ParentComponent` allows an optional `children` prop with the usual * type in JSX, `JSX.Element` (which allows elements, arrays, functions, etc.). * Use this for components that you want to accept children. */ export type ParentComponent

= Component>; /** * Extend props to require a `children` prop with the specified type. * Use this for components where you need a specific child type, * typically a function that receives specific argument types. * Note that all JSX are of the type `JSX.Element`. */ export type FlowProps

= P & { children: C }; /** * `FlowComponent` requires a `children` prop with the specified type. * Use this for components where you need a specific child type, * typically a function that receives specific argument types. * Note that all JSX are of the type `JSX.Element`. */ export type FlowComponent

= Component>; /** @deprecated: use `ParentProps` instead */ export type PropsWithChildren

= ParentProps

; export type ValidComponent = | keyof JSX.IntrinsicElements | Component | (string & {}); /** * Takes the props of the passed component and returns its type * * @example * ComponentProps // { mount?: Node; useShadow?: boolean; children: JSX.Element } * ComponentProps<'div'> // JSX.HTMLAttributes */ export type ComponentProps = T extends Component ? P : T extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[T] : Record; /** * Type of `props.ref`, for use in `Component` or `props` typing. * * @example Component<{ref: Ref}> */ export type Ref = T | ((val: T) => void); export function createComponent(Comp: Component, props: T): JSX.Element { if (hydrationEnabled) { if (sharedConfig.context) { const c = sharedConfig.context; setHydrateContext(nextHydrateContext()); const r = "_SOLID_DEV_" ? devComponent(Comp, props || ({} as T)) : untrack(() => Comp(props || ({} as T))); setHydrateContext(c); return r; } } if ("_SOLID_DEV_") return devComponent(Comp, props || ({} as T)); return untrack(() => Comp(props || ({} as T))); } function trueFn() { return true; } const propTraps: ProxyHandler<{ get: (k: string | number | symbol) => any; has: (k: string | number | symbol) => boolean; keys: () => string[]; }> = { get(_, property, receiver) { if (property === $PROXY) return receiver; return _.get(property); }, has(_, property) { return _.has(property); }, set: trueFn, deleteProperty: trueFn, getOwnPropertyDescriptor(_, property) { return { configurable: true, enumerable: true, get() { return _.get(property); }, set: trueFn, deleteProperty: trueFn }; }, ownKeys(_) { return _.keys(); } }; type DistributeOverride = T extends undefined ? F : T; type Override = T extends any ? U extends any ? { [K in keyof T]: K extends keyof U ? DistributeOverride : T[K]; } & { [K in keyof U]: K extends keyof T ? DistributeOverride : U[K]; } : T & U : T & U; type OverrideSpread = T extends any ? { [K in keyof ({ [K in keyof T]: any } & { [K in keyof U]?: any } & { [K in U extends any ? keyof U : keyof U]?: any; })]: K extends keyof T ? Exclude | T[K] : U extends any ? U[K & keyof U] : never; } : T & U; type Simplify = T extends any ? { [K in keyof T]: T[K] } : T; type _MergeProps = T extends [ infer Next | (() => infer Next), ...infer Rest ] ? _MergeProps> : T extends [...infer Rest, infer Next | (() => infer Next)] ? Override<_MergeProps, Next> : T extends [] ? Curr : T extends (infer I | (() => infer I))[] ? OverrideSpread : Curr; export type MergeProps = Simplify<_MergeProps>; function resolveSource(s: any) { return (s = typeof s === "function" ? s() : s) == null ? {} : s; } export function mergeProps(...sources: T): MergeProps { return new Proxy( { get(property: string | number | symbol) { for (let i = sources.length - 1; i >= 0; i--) { const v = resolveSource(sources[i])[property]; if (v !== undefined) return v; } }, has(property: string | number | symbol) { for (let i = sources.length - 1; i >= 0; i--) { if (property in resolveSource(sources[i])) return true; } return false; }, keys() { const keys = []; for (let i = 0; i < sources.length; i++) keys.push(...Object.keys(resolveSource(sources[i]))); return [...new Set(keys)]; } }, propTraps ) as unknown as MergeProps; } export type SplitProps = [ ...{ [P in keyof K]: P extends `${number}` ? Pick[number]> : never; }, Omit ]; export function splitProps( props: T, ...keys: K ): SplitProps { const blocked = new Set(keys.flat()); const descriptors = Object.getOwnPropertyDescriptors(props); const res = keys.map(k => { const clone = {}; for (let i = 0; i < k.length; i++) { const key = k[i]; Object.defineProperty( clone, key, descriptors[key] ? descriptors[key] : { get() { return props[key]; }, set() { return true; } } ); } return clone; }); res.push( new Proxy( { get(property: string | number | symbol) { return blocked.has(property as keyof T) ? undefined : props[property as keyof T]; }, has(property: string | number | symbol) { return blocked.has(property as keyof T) ? false : property in props; }, keys() { return Object.keys(props).filter(k => !blocked.has(k as keyof T)); } }, propTraps ) ); return res as SplitProps; } // lazy load a function component asynchronously export function lazy>( fn: () => Promise<{ default: T }> ): T & { preload: () => Promise<{ default: T }> } { let comp: () => T | undefined; let p: Promise<{ default: T }> | undefined; const wrap: T & { preload?: () => void } = ((props: any) => { const ctx = sharedConfig.context; if (ctx) { const [s, set] = createSignal(); (p || (p = fn())).then(mod => { setHydrateContext(ctx); set(() => mod.default); setHydrateContext(); }); comp = s; } else if (!comp) { const [s] = createResource(() => (p || (p = fn())).then(mod => mod.default)); comp = s; } else { const c = comp(); if (c) return c(props); } let Comp: T | undefined; return createMemo( () => (Comp = comp()) && untrack(() => { if ("_SOLID_DEV_") Object.assign(Comp!, { [$DEVCOMP]: true }); if (!ctx) return Comp!(props); const c = sharedConfig.context; setHydrateContext(ctx); const r = Comp!(props); setHydrateContext(c); return r; }) ); }) as T; wrap.preload = () => p || ((p = fn()).then(mod => (comp = () => mod.default)), p); return wrap as T & { preload: () => Promise<{ default: T }> }; } let counter = 0; export function createUniqueId(): string { const ctx = sharedConfig.context; return ctx ? `${ctx.id}${ctx.count++}` : `cl-${counter++}`; }