UNPKG

4.18 kBTypeScriptView Raw
1import {IS_SERVER} from "@gongt/ts-stl-library/check-environment";
2import {createLogger} from "@gongt/ts-stl-library/debug/create-logger";
3import {LOG_LEVEL} from "@gongt/ts-stl-library/debug/levels";
4import {GlobalVariable} from "@gongt/ts-stl-library/pattern/global-page-data";
5import * as React from "react";
6import {render} from "react-dom";
7import {inspect} from "util";
8import {GlobalContextProvider, ReactEmptyWrapper} from "./global-context";
9
10export interface IContextCreate {
11 (window: Window): GlobalVariable;
12}
13
14export const REACT_ROOT_ELEMENT_ID_VARNAME = 'REACT_ROOT_ELEMENT_ID';
15
16export interface WrapComponent {
17 componentName: string;
18 Component: React.ComponentClass<any>;
19 props: Function;
20}
21
22export type MainAppCreator = (global: GlobalVariable) => React.ReactElement<any>;
23
24export abstract class ReactRenderBase {
25 protected _getComponent: MainAppCreator;
26 protected wrapStack: WrapComponent[] = [];
27
28 setMainApp(component: MainAppCreator) {
29 this._getComponent = component;
30 }
31
32 protected getComponent(global: GlobalVariable): React.ReactElement<any> {
33 if (!this._getComponent) {
34 throw new TypeError(this.constructor.name + ': no main app.')
35 }
36 let component = this._getComponent(global);
37 this.wrapStack.forEach((props) => {
38 if (props) {
39 component = <ReactEmptyWrapper {...props}>{component}</ReactEmptyWrapper>;
40 } else {
41 const Component = props.Component;
42 component = <Component key={props.componentName}>{component}</Component>
43 }
44 });
45
46 return component;
47 }
48
49 wrapComponent<P>(componentName: string, Component: React.ComponentClass<P>, props?: (global: GlobalVariable) => P) {
50 this.wrapStack.push({componentName, Component, props});
51 }
52
53 abstract render(arg?: GlobalVariable): any;
54}
55
56export class ReactRender extends ReactRenderBase {
57 constructor() {
58 super();
59 }
60
61 render(global: GlobalVariable = new GlobalVariable()) {
62 if (!global.has(REACT_ROOT_ELEMENT_ID_VARNAME)) {
63 global.set(REACT_ROOT_ELEMENT_ID_VARNAME, 'react-root-random-' + (Math.random() * 10000).toFixed(0));
64 }
65
66 const react_root_id = global.get(REACT_ROOT_ELEMENT_ID_VARNAME);
67
68 let root = document.getElementById(react_root_id);
69 if (!root) {
70 root = document.createElement('DIV');
71 root.id = react_root_id;
72 document.body.appendChild(root);
73 }
74
75 const component = this.getComponent(global);
76
77 if (debugComponentEnabled) {
78 debugComponent(component);
79 }
80
81 render(
82 <GlobalContextProvider children={component} global={global}/>,
83 root,
84 );
85 }
86}
87
88const debug = createLogger(LOG_LEVEL.SILLY, 'react-render');
89const warn = createLogger(LOG_LEVEL.WARN, 'react-render');
90
91export function debugComponent(comp: React.ReactElement<any>) {
92 let itr = comp;
93 debug('ReactRender:');
94 while (itr && itr['type']) {
95 let Component = itr['type'];
96 if (Component === ReactEmptyWrapper) {
97 Component = itr['props'].Component;
98 if (!Component) {
99 warn('ReactEmptyWrapper without children: ', itr);
100 itr = itr['props'].children;
101 continue;
102 }
103 }
104 const name = Component['displayName'] || Component['name'] || Component;
105 const obj = Object.assign({}, itr.props);
106 const dbg = [];
107 wellknownProps.forEach((item) => {
108 if (obj[item]) {
109 delete obj[item];
110 dbg.push(pgreen);
111 } else {
112 dbg.push(pred);
113 }
114 dbg.push(item);
115 dbg.push(preset);
116 });
117
118 let ins: any;
119 if (IS_SERVER) {
120 ins = inspect(obj, false, 1, true).replace(/\s+/g, ' ');
121 if (process.stdout['columns']) {
122 ins = ins.substr(0, process.stdout['columns']);
123 }
124 } else {
125 ins = obj;
126 }
127 debug(messageStr, name, ...dbg, ins);
128 if (!itr['props']) {
129 break;
130 }
131 itr = itr['props'].children;
132 }
133}
134
135const pred = IS_SERVER? '\x1B[38;5;9m' : 'color:red';
136const pgreen = IS_SERVER? '\x1B[38;5;10m' : 'color:lime';
137const preset = IS_SERVER? '\x1B[0m' : 'color:black';
138
139const wellknownProps = ['global', 'store', 'children', 'Component'];
140const pcolor = IS_SERVER? '%s' : '%c';
141const messagePart = wellknownProps.map(e => pcolor + '%s' + pcolor).join(', ');
142const messageStr = IS_SERVER
143 ? '> %s (' + messagePart + ')\n%s'
144 : '> %s (' + messagePart + ', %O)';
145
146export const debugComponentEnabled = debug.enabled;
147
\No newline at end of file