Skip to content

Instantly share code, notes, and snippets.

@mikeplus64
Last active July 20, 2020 14:31
Show Gist options
  • Save mikeplus64/29f4ed6564d1a229f4f10593f39e1c7c to your computer and use it in GitHub Desktop.
Save mikeplus64/29f4ed6564d1a229f4f10593f39e1c7c to your computer and use it in GitHub Desktop.
router5 + typescript + effort
import { UnionToIntersection, DeepWritable } from 'ts-essentials';
import { State as RouteState } from 'router5';
import { Switch, createState, createEffect, createMemo } from 'solid-js';
import useRoute, { MatchRoute } from './context';
import { Routes } from './definition';
export type RenderTreeOf<Tree> =
Owned<Tree> | RenderNode &
UnionToIntersection<
Tree extends readonly (infer Node)[]
? Node extends { name: infer Name, children?: infer Children }
? Name extends (string | number | symbol)
? Children extends {}
? { [K in Name]?: RenderTreeOf<Children> }
: { [K in Name]?: Owned<Children> | RenderNode }
: never
: never
: never
>;
interface RenderNode {
render?(props: { children: JSX.Element }): JSX.Element,
fallback?(props: { children: JSX.Element }): JSX.Element,
};
export type OwnedBy<Tree, Props> =
UnionToIntersection<
Tree extends readonly (infer Node)[]
? Node extends { name: infer Name, children?: infer Children }
? Name extends (string | number | symbol)
? Children extends {}
? { [K in Name]?: GetProps<Props> & OwnedBy<Children, Props> }
: { [K in Name]?: GetProps<Props> }
: never
: never
: never
>;
interface OwnedOps<Tree, Props> {
render: (props: Props) => JSX.Element,
defaultProps: Props,
props: OwnedBy<Tree, Props>,
}
type Owned<Tree> =
<R>(cont: <Props>(self: OwnedOps<Tree, Props>) => R) => R;
type GetProps<Props> =
{ [K in keyof Props]?: () => Props[K] };
export function passthru<T>(props: { children: T }): T {
return props.children;
}
export default function sm<R extends RenderTreeLike>(tree: R): JSX.Element {
const route = useRoute();
function traverseHydrate<Props>(
path0: string[],
node0: GetPropsLike<Props>,
render: (props: Props) => JSX.Element,
defaultProps: Props,
): JSX.Element {
const [state, setState] = createState(defaultProps);
function populate(
path: string[],
node: GetPropsLike<Props>,
next: Partial<Props>,
count: number,
): number {
for (const key in node) {
const gp = (node as GetProps<Props>)[key as keyof Props];
if (typeof gp === 'function') {
const value = gp();
next[key as keyof Props] = value;
count ++;
continue;
}
if (gp !== undefined) {
if (path[0] === key) {
return populate(path.slice(1), gp as any, next, count);
}
}
}
return count;
}
const getPathSuffix = createMemo<[string, string[]]>(() => {
const name = route().name;
const p = name.split('.');
p.splice(0, path0.length);
return [name, p];
}, undefined, (a, b) => a && a[0] === b[0]);
createEffect(() => {
const next: Partial<Props> = {};
if (populate(getPathSuffix()[1], node0, next, 0) > 0) {
setState(next);
}
});
return render(state as Props);
}
function traverse(
path: string[],
node: RenderTreeLike,
): JSX.Element {
if (typeof node === 'function') {
return node(function <Props>(owned: OwnedOpsLike<Props>) {
const { props, render, defaultProps } = owned;
return traverseHydrate(path, props, render, defaultProps);
});
}
const children: JSX.Element = [];
let { render: Render, fallback, ...routes } = node;
if (Render === undefined) { Render = passthru; }
if (typeof Render !== 'function') { return undefined; }
for (const key in routes) {
const next = [...path, key];
const child = (routes as any)[key];
children.push(MatchRoute({
prefix: key,
children: () => traverse(next, child),
}));
}
return Render({
children: Switch({
fallback: typeof fallback === 'function' ? fallback({ children }) : undefined,
children,
}),
});
}
return traverse([], tree);
}
type GetPropsLike<Props> =
{ [k: string]: GetPropsLike<Props> } & GetProps<Props>;
interface OwnedOpsLike<Props> {
render: (props: Props) => JSX.Element,
defaultProps: Props,
props: GetPropsLike<Props>
}
type OwnedLike =
(cont: <Props>(self: OwnedOpsLike<Props>) => any) => any;
export type RenderTreeLike
= OwnedLike
| (RenderNode & { [k: string]: RenderTreeLike });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment