-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrenderer.tsx
119 lines (101 loc) · 4.14 KB
/
renderer.tsx
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import Isomorphism from '.';
import * as ReactDomServer from 'react-dom/server';
import * as React from 'react';
interface Renderer {
<T> (target: Isomorphism<T>, parameter: Renderer.Parameter<T>, output: NodeJS.WritableStream): void
}
namespace Renderer {
export interface MetadataTable {
[name: string]: string
}
export interface Parameter<T> {
title: string;
locale?: string;
metadata?: MetadataTable;
data: T;
}
export const DocumentType = {
HTML5: '<!DOCTYPE html>\n'
}
}
namespace Renderer {
export class Builder {
private assetsUrlMap: (pageName: string)=> string[];
private docType: string = Renderer.DocumentType.HTML5;
private enableXHTML: boolean = false;
private metadata: Renderer.MetadataTable = {};
setAssetsUrlMap(map: (pageName: string)=> string[]): this {
this.assetsUrlMap = map;
return this;
}
setDocumentType(docType: string): this {
if (docType[docType.length-1] != '\n')
docType = docType + '\n';
this.docType = docType;
return this;
}
setEnableXHTML(enabled: boolean): this{
this.enableXHTML = enabled;
return this;
}
setMetadata(name: string, value: string): this{
this.metadata[name] = value;
return this;
}
build(): Renderer {
let self = this; // Typescript bug??? workaround for <T>()=> {...} recognized as JSX syntax
return function <T> (this: undefined,
target: Isomorphism<T>,
args: Renderer.Parameter<T>,
output: NodeJS.WritableStream){
let htmlAttrs = {};
if (self.enableXHTML){
htmlAttrs['xmlns'] = "http://www.w3.org/1999/xhtml";
if (args.locale)
htmlAttrs['xml:lang'] = args.locale
} else {
if (args.locale)
htmlAttrs['lang'] = args.locale
}
let meta = self.metadata;
let content = ReactDomServer.renderToString(target.render(args.data));
let json = JSON.stringify(args.data);
let argumentHolder;
if (self.enableXHTML) {
// We don't need to escape " and ' in > in script content
// Only escape & and < manually to minify the size
let __html = json.replace(/&/g, '&').replace(/</g, '<');
argumentHolder = <script id='x-render-args-holder'
type='application/json'
dangerouslySetInnerHTML={{__html}} />
} else {
argumentHolder = <meta id='x-render-args-holder' name='x-render-args-holder' content={json}/>
}
let htmlElement = (
<html {...htmlAttrs} >
<head>
<title>{args.title}</title>
{argumentHolder}
{
Object.keys(meta).map((key, idx) => {
return <meta key={`meta-${idx}`} name={key} content={meta[key]}/>;
})
}
{
self.assetsUrlMap((target.pageName)).map((scriptName, idx)=> {
return <script key={`script-${idx}`} type='application/javascript' src={scriptName}/>
})
}
</head>
<body>
<div id="x-react-container" dangerouslySetInnerHTML={{__html: content}} />
</body>
</html>
);
output.write(self.docType);
ReactDomServer.renderToNodeStream(htmlElement).pipe(output);
}
}
}
}
export default Renderer;