-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathjsx.mjs
180 lines (150 loc) · 4.45 KB
/
jsx.mjs
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
const assetUriPathPlaceholder = /^__NAKEDJSX_ASSET_DIR__/;
const assetAttributeNames = new Set(['data', 'srcset', 'src', 'href']);
//
// Wrap Element.appendChild() so that it can add an array of elements,
// which allows a JSX fragment to be passed to appendChild.
// Additionally, strings are converted to text nodes.
//
const originalAppendChild = Element.prototype.appendChild;
Element.prototype.appendChild =
function(child)
{
if (child instanceof Node)
return originalAppendChild.call(this, child);
else if (Array.isArray(child))
{
for (const childArrayMember of child)
this.appendChild(childArrayMember);
return child;
}
else if (child === false || child === null || child === undefined)
return null;
else
return originalAppendChild.call(this, document.createTextNode(child.toString()));
};
export function __nakedjsx__createElement(tag, props, ...children)
{
props = props || {};
if (typeof tag === "function")
{
// Make child elements selectively placeable via {props.children}
props.children = children;
return tag(props);
}
//
// Support the <raw-content> tag for injecting raw HTML
//
if (tag === 'raw-content')
{
const dummy = document.createElement('div');
dummy.innerHTML = props.content;
return [...dummy.children];
}
//
// We're dealing with a regular HTML-like tag, not a JSX function.
//
// SVG and mathml elements need to be created with a namespace.
//
let element;
if (props.xmlns)
element = document.createElementNS(props.xmlns, tag);
else
element = document.createElement(tag);
for (const [name, value] of Object.entries(props))
{
if (name.startsWith('on'))
{
const lowercaseName = name.toLowerCase();
if (lowercaseName in window)
{
element.addEventListener(lowercaseName.substring(2), value);
continue;
}
}
//
// Skip attributes with a valud of false, null, or undefined.
//
if (value === false || value === null || value === undefined)
continue;
//
// Boolean 'true' attribute values are converted to the presence of
// an attribute with no assigned value.
//
if (value === true)
{
element.setAttribute(name, '');
continue;
}
//
// Support capturing a reference to the created element.
//
if (name == 'ref')
{
if (typeof value === 'object')
value.current = element;
else
console.error('ref must be an object');
continue;
}
//
// Imported assets need to be resolved to their final path
//
if (assetAttributeNames.has(name))
if (typeof value === 'string')
if (assetUriPathPlaceholder.test(value))
{
element.setAttribute(name, nakedjsx.assetPath(value));
continue;
}
//
// Default attribute assignment
//
element.setAttribute(name, value);
};
if (props.xmlns)
{
//
// HACK - we need to recreate all the children with the same namespace
// as the parent element. Otherwise, SVG and MathML elements
// will not render correctly.
//
// It would be better to handle this as a babel plugin.
//
let innerHTML = '';
for (const child of children)
{
if (child instanceof Node)
innerHTML += child.outerHTML;
else
innerHTML += child;
}
element.innerHTML = innerHTML;
}
else
{
for (const child of children)
element.appendChild(child);
}
return element;
}
export function __nakedjsx__createFragment(props)
{
return props.children;
}
function createRef()
{
//
// A Ref is a container that recieves a reference to
// a created HTML element.
//
return {};
}
function assetPath(assetPath)
{
return assetPath.replace(assetUriPathPlaceholder, relativeAssetRoot);
}
export const nakedjsx =
{
createRef,
assetPath
};