1 | const React = require('react');
|
2 | const createClass = require('create-react-class');
|
3 |
|
4 | const reduce = (obj, fn, init)=>Object.keys(obj).reduce((acc, key)=>fn(acc, obj[key], key), init);
|
5 | const map = (obj, fn)=>Object.keys(obj).map((key)=>fn(obj[key], key));
|
6 |
|
7 | let Storage;
|
8 |
|
9 | const mapProps = (props)=>map(props, (val, key)=>`${key}="${val}"`).join(' ');
|
10 | const processData = (data)=>{
|
11 | return reduce(data, (acc, val, key)=>{
|
12 | if(key == 'type') key = '@type';
|
13 | if(key == 'context') key = '@context';
|
14 | acc[key] = (typeof val == 'object'
|
15 | ? processData(val)
|
16 | : val
|
17 | );
|
18 | return acc;
|
19 | }, {});
|
20 | };
|
21 |
|
22 | const HeadTags = {
|
23 | Title : createClass({
|
24 | componentWillMount(){ Storage.title = this.props.children; },
|
25 | render(){
|
26 | if(typeof document !== 'undefined') document.title = this.props.children;
|
27 | return null;
|
28 | }
|
29 | }),
|
30 | Description : createClass({
|
31 | componentWillMount(){ Storage.description = this.props.children; },
|
32 | render(){ return null; }
|
33 | }),
|
34 | Favicon : createClass({
|
35 | getDefaultProps(){ return { type : 'image/png', href : ''}},
|
36 | componentWillMount(){ Storage.favicon = this.props; },
|
37 | render(){
|
38 | if(typeof document !== 'undefined') document.getElementById('favicon').href=this.props.href;
|
39 | return null;
|
40 | }
|
41 | }),
|
42 | Noscript : createClass({
|
43 | componentWillMount(){ Storage.noscript.push(this.props.children); },
|
44 | render(){ return null; }
|
45 | }),
|
46 | Script : createClass({
|
47 | getDefaultProps(){ return { id : '', src : ''}},
|
48 | componentWillMount(){ Storage.script.push(this.props); },
|
49 | render(){ return null; }
|
50 | }),
|
51 | Meta : createClass({
|
52 | componentWillMount(){
|
53 | const addTag = (props)=>{
|
54 | (props.property || props.name)
|
55 | ? Storage.namedMeta[props.property || props.name] = props
|
56 | : Storage.unnamedMeta.push(props);
|
57 | }
|
58 | this.props.bulk
|
59 | ? map(this.props.bulk, (content, property)=>addTag({ content, property }))
|
60 | : addTag(this.props);
|
61 | },
|
62 | render(){ return null; }
|
63 | }),
|
64 | Structured : createClass({
|
65 | componentWillMount(){ Storage.structuredData = processData(this.props.data); },
|
66 | render(){ return null; }
|
67 | }),
|
68 | flush : ()=>{
|
69 | Storage = {
|
70 | title : null,
|
71 | description : null,
|
72 | namedMeta : {},
|
73 | unnamedMeta : [],
|
74 | noscript : [],
|
75 | script : []
|
76 | }
|
77 | },
|
78 | generate : ()=>{
|
79 | let res = [];
|
80 | if(Storage.title){
|
81 | res.push(`<title>${Storage.title}</title>`);
|
82 | }
|
83 | if(Storage.favicon){
|
84 | res.push(`<link id='favicon' rel='shortcut icon' type='${Storage.favicon.type}' href='${Storage.favicon.href}' />`);
|
85 | }
|
86 | if(Storage.description){
|
87 | res.push(`<meta content='${Storage.description}' name='description' />`);
|
88 | }
|
89 | const Meta = Object.values(Storage.namedMeta).concat(Storage.unnamedMeta);
|
90 | if(Meta && Meta.length){
|
91 | res = res.concat(Meta.reverse().map((metaProps)=>`<meta ${mapProps(metaProps)} />`));
|
92 | }
|
93 | if(Storage.noscript && Storage.noscript.length){
|
94 | res = res.concat(`<noscript>${Storage.noscript.join('\n')}</noscript>`);
|
95 | }
|
96 | if(Storage.script && Storage.script.length){
|
97 | res = res.concat(Storage.script.map((script)=>{
|
98 | return `<script id='${script.id}' src='${script.src}'>${script.children}</script>`;
|
99 | }).join('\n'));
|
100 | }
|
101 | if(Storage.structuredData){
|
102 | res.push(`<script type='application/ld+json'>${JSON.stringify(Storage.structuredData, null, ' ')}</script>`);
|
103 | }
|
104 | HeadTags.flush();
|
105 | return res.join('\n');
|
106 | }
|
107 | };
|
108 |
|
109 | HeadTags.flush();
|
110 | module.exports = HeadTags; |
\ | No newline at end of file |