-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
103 lines (89 loc) · 2.89 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import flattenDeep from 'lodash.flattendeep';
export type SlackSpec = {} | string;
const pruneFields = <R>(o: {}): Partial<R> =>
o
? Object.keys(o).reduce(
(obj, k) => (o[k] !== undefined ? { ...obj, [k]: o[k] } : obj),
{}
)
: undefined;
type Props<P> = { children?: unknown } & P;
export type FC<P extends {}, R extends any> = (props: Props<P>) => R;
const resolveDeep = async (thing: any) => {
if (!thing) {
return thing;
} else if (Array.isArray(thing)) {
return await Promise.all(thing.map(item => resolveDeep(item)));
} else if (thing.__proto__ === Promise.prototype) {
return await Promise.resolve(thing);
} else if (typeof thing === 'object') {
if (Object.getPrototypeOf(thing).constructor.toString().match(/class/)) {
return await Promise.resolve(thing);
}
const resolvedPairs = await Promise.all(
Object.keys(thing).map(async key => [key, await resolveDeep(thing[key])])
);
return Object.fromEntries(resolvedPairs);
} else if (typeof thing === 'function') {
return await Promise.resolve(thing);
} else {
return thing;
}
};
// for now, this is a hack to help with typescript checking.
// if you wrap a top-level JSX element with `await render(<>)`,
// it's a better experience than just warpping with `await (<>)`.
// in the future, most of the promise awaiting should happen in
// render. h should just return the tree of JSX elements, and
// render should walk the tree and process/await/execute hooks,
// etc.
export const render = async (
rootElement: Promise<slack.JSX.Element>
): Promise<any> => await rootElement;
export namespace slack {
export const h = async (
node: FC<{}, any>,
props: Props<{}>,
...children: any[]
): Promise<JSX.Element> => {
if (typeof node !== 'function') {
throw new Error('node not an FC');
}
const resolvedProps = await resolveDeep(props || {});
const spec = await node({
...resolvedProps,
children: flattenDeep(
await Promise.all(
children.map(async child => {
if (Array.isArray(child)) {
return await Promise.all(flattenDeep(child));
} else if (
typeof child === 'function' ||
typeof child === 'object'
) {
return await Promise.resolve(child);
}
return child;
})
)
).filter(child => !!child),
});
return typeof spec === 'string' || Array.isArray(spec)
? spec
: pruneFields(spec);
};
export const Fragment = ({ children }): JSX.Element => {
return children as JSX.Element;
};
export namespace JSX {
export type Element = any;
export interface ElementAttributesProperty {
props: {};
}
export interface ElementChildrenAttribute {
children: {};
}
}
}
export * from './components';
export { renderMarkdown } from './renderMarkdown';