preact-render-to-string 5.2.1 → 5.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs.js +1 -1
- package/dist/commonjs.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.module.js +1 -1
- package/dist/index.module.js.map +1 -1
- package/dist/jsx-entry.js +1 -1
- package/dist/jsx-entry.js.map +1 -1
- package/dist/jsx.js.map +1 -1
- package/dist/jsx.mjs +1 -1
- package/dist/jsx.modern.js +1 -1
- package/dist/jsx.modern.js.map +1 -1
- package/dist/jsx.module.js +1 -1
- package/dist/jsx.module.js.map +1 -1
- package/package.json +5 -4
- package/src/index.js +339 -42
- package/src/util.js +28 -11
package/src/index.js
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
indent,
|
|
4
4
|
isLargeString,
|
|
5
5
|
styleObjToCss,
|
|
6
|
-
assign,
|
|
7
6
|
getChildren
|
|
8
7
|
} from './util';
|
|
9
8
|
import { options, Fragment } from 'preact';
|
|
@@ -15,10 +14,10 @@ const SHALLOW = { shallow: true };
|
|
|
15
14
|
// components without names, kept as a hash for later comparison to return consistent UnnamedComponentXX names.
|
|
16
15
|
const UNNAMED = [];
|
|
17
16
|
|
|
18
|
-
const VOID_ELEMENTS =
|
|
19
|
-
/^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;
|
|
17
|
+
const VOID_ELEMENTS = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;
|
|
20
18
|
|
|
21
19
|
const UNSAFE_NAME = /[\s\n\\/='"\0<>]/;
|
|
20
|
+
const XLINK = /^xlink:?./;
|
|
22
21
|
|
|
23
22
|
function markAsDirty() {
|
|
24
23
|
this.__d = true;
|
|
@@ -59,7 +58,12 @@ function renderToString(vnode, context, opts) {
|
|
|
59
58
|
const previousSkipEffects = options.__s;
|
|
60
59
|
options.__s = true;
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
let res;
|
|
62
|
+
if (opts.pretty || opts.sortAttributes) {
|
|
63
|
+
res = _renderToStringPretty(vnode, context, opts);
|
|
64
|
+
} else {
|
|
65
|
+
res = _renderToString(vnode, context, opts);
|
|
66
|
+
}
|
|
63
67
|
|
|
64
68
|
// options._commit, we don't schedule any effects in this library right now,
|
|
65
69
|
// so we can pass an empty queue to this hook.
|
|
@@ -69,6 +73,32 @@ function renderToString(vnode, context, opts) {
|
|
|
69
73
|
return res;
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
function createComponent(vnode, context) {
|
|
77
|
+
return {
|
|
78
|
+
__v: vnode,
|
|
79
|
+
context,
|
|
80
|
+
props: vnode.props,
|
|
81
|
+
// silently drop state updates
|
|
82
|
+
setState: markAsDirty,
|
|
83
|
+
forceUpdate: markAsDirty,
|
|
84
|
+
__d: true,
|
|
85
|
+
// hooks
|
|
86
|
+
__h: []
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Necessary for createContext api. Setting this property will pass
|
|
91
|
+
// the context value as `this.context` just for this component.
|
|
92
|
+
function getContext(nodeName, context) {
|
|
93
|
+
let cxType = nodeName.contextType;
|
|
94
|
+
let provider = cxType && context[cxType.__c];
|
|
95
|
+
return cxType != null
|
|
96
|
+
? provider
|
|
97
|
+
? provider.props.value
|
|
98
|
+
: cxType.__
|
|
99
|
+
: context;
|
|
100
|
+
}
|
|
101
|
+
|
|
72
102
|
/** The default export is an alias of `render()`. */
|
|
73
103
|
function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
74
104
|
if (vnode == null || typeof vnode === 'boolean') {
|
|
@@ -80,6 +110,292 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
80
110
|
return encodeEntities(vnode);
|
|
81
111
|
}
|
|
82
112
|
|
|
113
|
+
if (Array.isArray(vnode)) {
|
|
114
|
+
const rendered = [];
|
|
115
|
+
for (let i = 0; i < vnode.length; i++) {
|
|
116
|
+
rendered.push(
|
|
117
|
+
_renderToString(vnode[i], context, opts, inner, isSvgMode, selectValue)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return rendered.join('');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let nodeName = vnode.type,
|
|
124
|
+
props = vnode.props,
|
|
125
|
+
isComponent = false;
|
|
126
|
+
|
|
127
|
+
// components
|
|
128
|
+
if (typeof nodeName === 'function') {
|
|
129
|
+
isComponent = true;
|
|
130
|
+
if (opts.shallow && (inner || opts.renderRootComponent === false)) {
|
|
131
|
+
nodeName = getComponentName(nodeName);
|
|
132
|
+
} else if (nodeName === Fragment) {
|
|
133
|
+
const children = [];
|
|
134
|
+
getChildren(children, vnode.props.children);
|
|
135
|
+
return _renderToString(
|
|
136
|
+
children,
|
|
137
|
+
context,
|
|
138
|
+
opts,
|
|
139
|
+
opts.shallowHighOrder !== false,
|
|
140
|
+
isSvgMode,
|
|
141
|
+
selectValue
|
|
142
|
+
);
|
|
143
|
+
} else {
|
|
144
|
+
let rendered;
|
|
145
|
+
|
|
146
|
+
let c = (vnode.__c = createComponent(vnode, context));
|
|
147
|
+
|
|
148
|
+
// options._diff
|
|
149
|
+
if (options.__b) options.__b(vnode);
|
|
150
|
+
|
|
151
|
+
// options._render
|
|
152
|
+
let renderHook = options.__r;
|
|
153
|
+
|
|
154
|
+
if (
|
|
155
|
+
!nodeName.prototype ||
|
|
156
|
+
typeof nodeName.prototype.render !== 'function'
|
|
157
|
+
) {
|
|
158
|
+
let cctx = getContext(nodeName, context);
|
|
159
|
+
|
|
160
|
+
// If a hook invokes setState() to invalidate the component during rendering,
|
|
161
|
+
// re-render it up to 25 times to allow "settling" of memoized states.
|
|
162
|
+
// Note:
|
|
163
|
+
// This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
|
|
164
|
+
// https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
|
|
165
|
+
let count = 0;
|
|
166
|
+
while (c.__d && count++ < 25) {
|
|
167
|
+
c.__d = false;
|
|
168
|
+
|
|
169
|
+
if (renderHook) renderHook(vnode);
|
|
170
|
+
|
|
171
|
+
// stateless functional components
|
|
172
|
+
rendered = nodeName.call(vnode.__c, props, cctx);
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
let cctx = getContext(nodeName, context);
|
|
176
|
+
|
|
177
|
+
// c = new nodeName(props, context);
|
|
178
|
+
c = vnode.__c = new nodeName(props, cctx);
|
|
179
|
+
c.__v = vnode;
|
|
180
|
+
// turn off stateful re-rendering:
|
|
181
|
+
c._dirty = c.__d = true;
|
|
182
|
+
c.props = props;
|
|
183
|
+
if (c.state == null) c.state = {};
|
|
184
|
+
|
|
185
|
+
if (c._nextState == null && c.__s == null) {
|
|
186
|
+
c._nextState = c.__s = c.state;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
c.context = cctx;
|
|
190
|
+
if (nodeName.getDerivedStateFromProps)
|
|
191
|
+
c.state = Object.assign(
|
|
192
|
+
{},
|
|
193
|
+
c.state,
|
|
194
|
+
nodeName.getDerivedStateFromProps(c.props, c.state)
|
|
195
|
+
);
|
|
196
|
+
else if (c.componentWillMount) {
|
|
197
|
+
c.componentWillMount();
|
|
198
|
+
|
|
199
|
+
// If the user called setState in cWM we need to flush pending,
|
|
200
|
+
// state updates. This is the same behaviour in React.
|
|
201
|
+
c.state =
|
|
202
|
+
c._nextState !== c.state
|
|
203
|
+
? c._nextState
|
|
204
|
+
: c.__s !== c.state
|
|
205
|
+
? c.__s
|
|
206
|
+
: c.state;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (renderHook) renderHook(vnode);
|
|
210
|
+
|
|
211
|
+
rendered = c.render(c.props, c.state, c.context);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (c.getChildContext) {
|
|
215
|
+
context = Object.assign({}, context, c.getChildContext());
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (options.diffed) options.diffed(vnode);
|
|
219
|
+
return _renderToString(
|
|
220
|
+
rendered,
|
|
221
|
+
context,
|
|
222
|
+
opts,
|
|
223
|
+
opts.shallowHighOrder !== false,
|
|
224
|
+
isSvgMode,
|
|
225
|
+
selectValue
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// render JSX to HTML
|
|
231
|
+
let s = `<${nodeName}`,
|
|
232
|
+
propChildren,
|
|
233
|
+
html;
|
|
234
|
+
|
|
235
|
+
if (props) {
|
|
236
|
+
for (let name in props) {
|
|
237
|
+
let v = props[name];
|
|
238
|
+
if (name === 'children') {
|
|
239
|
+
propChildren = v;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (UNSAFE_NAME.test(name)) continue;
|
|
244
|
+
|
|
245
|
+
if (
|
|
246
|
+
!(opts && opts.allAttributes) &&
|
|
247
|
+
(name === 'key' ||
|
|
248
|
+
name === 'ref' ||
|
|
249
|
+
name === '__self' ||
|
|
250
|
+
name === '__source')
|
|
251
|
+
)
|
|
252
|
+
continue;
|
|
253
|
+
|
|
254
|
+
if (name === 'defaultValue') {
|
|
255
|
+
name = 'value';
|
|
256
|
+
} else if (name === 'defaultChecked') {
|
|
257
|
+
name = 'checked';
|
|
258
|
+
} else if (name === 'defaultSelected') {
|
|
259
|
+
name = 'selected';
|
|
260
|
+
} else if (name === 'className') {
|
|
261
|
+
if (typeof props.class !== 'undefined') continue;
|
|
262
|
+
name = 'class';
|
|
263
|
+
} else if (isSvgMode && XLINK.test(name)) {
|
|
264
|
+
name = name.toLowerCase().replace(/^xlink:?/, 'xlink:');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (name === 'htmlFor') {
|
|
268
|
+
if (props.for) continue;
|
|
269
|
+
name = 'for';
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (name === 'style' && v && typeof v === 'object') {
|
|
273
|
+
v = styleObjToCss(v);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// always use string values instead of booleans for aria attributes
|
|
277
|
+
// also see https://github.com/preactjs/preact/pull/2347/files
|
|
278
|
+
if (name[0] === 'a' && name['1'] === 'r' && typeof v === 'boolean') {
|
|
279
|
+
v = String(v);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
let hooked =
|
|
283
|
+
opts.attributeHook &&
|
|
284
|
+
opts.attributeHook(name, v, context, opts, isComponent);
|
|
285
|
+
if (hooked || hooked === '') {
|
|
286
|
+
s = s + hooked;
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (name === 'dangerouslySetInnerHTML') {
|
|
291
|
+
html = v && v.__html;
|
|
292
|
+
} else if (nodeName === 'textarea' && name === 'value') {
|
|
293
|
+
// <textarea value="a&b"> --> <textarea>a&b</textarea>
|
|
294
|
+
propChildren = v;
|
|
295
|
+
} else if ((v || v === 0 || v === '') && typeof v !== 'function') {
|
|
296
|
+
if (v === true || v === '') {
|
|
297
|
+
v = name;
|
|
298
|
+
// in non-xml mode, allow boolean attributes
|
|
299
|
+
if (!opts || !opts.xml) {
|
|
300
|
+
s = s + ' ' + name;
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (name === 'value') {
|
|
306
|
+
if (nodeName === 'select') {
|
|
307
|
+
selectValue = v;
|
|
308
|
+
continue;
|
|
309
|
+
} else if (
|
|
310
|
+
// If we're looking at an <option> and it's the currently selected one
|
|
311
|
+
nodeName === 'option' &&
|
|
312
|
+
selectValue == v &&
|
|
313
|
+
// and the <option> doesn't already have a selected attribute on it
|
|
314
|
+
typeof props.selected === 'undefined'
|
|
315
|
+
) {
|
|
316
|
+
s = `${s} selected`;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
s = s + ` ${name}="${encodeEntities(v)}"`;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
s = s + '>';
|
|
325
|
+
|
|
326
|
+
if (UNSAFE_NAME.test(nodeName))
|
|
327
|
+
throw new Error(`${nodeName} is not a valid HTML tag name in ${s}`);
|
|
328
|
+
|
|
329
|
+
let isVoid =
|
|
330
|
+
VOID_ELEMENTS.test(nodeName) ||
|
|
331
|
+
(opts.voidElements && opts.voidElements.test(nodeName));
|
|
332
|
+
let pieces = '';
|
|
333
|
+
|
|
334
|
+
let children;
|
|
335
|
+
if (html) {
|
|
336
|
+
s = s + html;
|
|
337
|
+
} else if (
|
|
338
|
+
propChildren != null &&
|
|
339
|
+
getChildren((children = []), propChildren).length
|
|
340
|
+
) {
|
|
341
|
+
for (let i = 0; i < children.length; i++) {
|
|
342
|
+
let child = children[i];
|
|
343
|
+
|
|
344
|
+
if (child != null && child !== false) {
|
|
345
|
+
let childSvgMode =
|
|
346
|
+
nodeName === 'svg'
|
|
347
|
+
? true
|
|
348
|
+
: nodeName === 'foreignObject'
|
|
349
|
+
? false
|
|
350
|
+
: isSvgMode,
|
|
351
|
+
ret = _renderToString(
|
|
352
|
+
child,
|
|
353
|
+
context,
|
|
354
|
+
opts,
|
|
355
|
+
true,
|
|
356
|
+
childSvgMode,
|
|
357
|
+
selectValue
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// Skip if we received an empty string
|
|
361
|
+
if (ret) {
|
|
362
|
+
pieces = pieces + ret;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (pieces.length || html) {
|
|
369
|
+
s = s + pieces;
|
|
370
|
+
} else if (opts && opts.xml) {
|
|
371
|
+
return s.substring(0, s.length - 1) + ' />';
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (isVoid && !children && !html) {
|
|
375
|
+
return s.replace(/>$/, ' />');
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return `${s}</${nodeName}>`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/** The default export is an alias of `render()`. */
|
|
382
|
+
function _renderToStringPretty(
|
|
383
|
+
vnode,
|
|
384
|
+
context,
|
|
385
|
+
opts,
|
|
386
|
+
inner,
|
|
387
|
+
isSvgMode,
|
|
388
|
+
selectValue
|
|
389
|
+
) {
|
|
390
|
+
if (vnode == null || typeof vnode === 'boolean') {
|
|
391
|
+
return '';
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// #text nodes
|
|
395
|
+
if (typeof vnode !== 'object') {
|
|
396
|
+
return encodeEntities(vnode);
|
|
397
|
+
}
|
|
398
|
+
|
|
83
399
|
let pretty = opts.pretty,
|
|
84
400
|
indentChar = pretty && typeof pretty === 'string' ? pretty : '\t';
|
|
85
401
|
|
|
@@ -89,7 +405,14 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
89
405
|
if (pretty && i > 0) rendered = rendered + '\n';
|
|
90
406
|
rendered =
|
|
91
407
|
rendered +
|
|
92
|
-
|
|
408
|
+
_renderToStringPretty(
|
|
409
|
+
vnode[i],
|
|
410
|
+
context,
|
|
411
|
+
opts,
|
|
412
|
+
inner,
|
|
413
|
+
isSvgMode,
|
|
414
|
+
selectValue
|
|
415
|
+
);
|
|
93
416
|
}
|
|
94
417
|
return rendered;
|
|
95
418
|
}
|
|
@@ -106,7 +429,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
106
429
|
} else if (nodeName === Fragment) {
|
|
107
430
|
const children = [];
|
|
108
431
|
getChildren(children, vnode.props.children);
|
|
109
|
-
return
|
|
432
|
+
return _renderToStringPretty(
|
|
110
433
|
children,
|
|
111
434
|
context,
|
|
112
435
|
opts,
|
|
@@ -117,17 +440,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
117
440
|
} else {
|
|
118
441
|
let rendered;
|
|
119
442
|
|
|
120
|
-
let c = (vnode.__c =
|
|
121
|
-
__v: vnode,
|
|
122
|
-
context,
|
|
123
|
-
props: vnode.props,
|
|
124
|
-
// silently drop state updates
|
|
125
|
-
setState: markAsDirty,
|
|
126
|
-
forceUpdate: markAsDirty,
|
|
127
|
-
__d: true,
|
|
128
|
-
// hooks
|
|
129
|
-
__h: []
|
|
130
|
-
});
|
|
443
|
+
let c = (vnode.__c = createComponent(vnode, context));
|
|
131
444
|
|
|
132
445
|
// options._diff
|
|
133
446
|
if (options.__b) options.__b(vnode);
|
|
@@ -139,16 +452,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
139
452
|
!nodeName.prototype ||
|
|
140
453
|
typeof nodeName.prototype.render !== 'function'
|
|
141
454
|
) {
|
|
142
|
-
|
|
143
|
-
// the context value as `this.context` just for this component.
|
|
144
|
-
let cxType = nodeName.contextType;
|
|
145
|
-
let provider = cxType && context[cxType.__c];
|
|
146
|
-
let cctx =
|
|
147
|
-
cxType != null
|
|
148
|
-
? provider
|
|
149
|
-
? provider.props.value
|
|
150
|
-
: cxType.__
|
|
151
|
-
: context;
|
|
455
|
+
let cctx = getContext(nodeName, context);
|
|
152
456
|
|
|
153
457
|
// If a hook invokes setState() to invalidate the component during rendering,
|
|
154
458
|
// re-render it up to 25 times to allow "settling" of memoized states.
|
|
@@ -165,15 +469,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
165
469
|
rendered = nodeName.call(vnode.__c, props, cctx);
|
|
166
470
|
}
|
|
167
471
|
} else {
|
|
168
|
-
|
|
169
|
-
let cxType = nodeName.contextType;
|
|
170
|
-
let provider = cxType && context[cxType.__c];
|
|
171
|
-
let cctx =
|
|
172
|
-
cxType != null
|
|
173
|
-
? provider
|
|
174
|
-
? provider.props.value
|
|
175
|
-
: cxType.__
|
|
176
|
-
: context;
|
|
472
|
+
let cctx = getContext(nodeName, context);
|
|
177
473
|
|
|
178
474
|
// c = new nodeName(props, context);
|
|
179
475
|
c = vnode.__c = new nodeName(props, cctx);
|
|
@@ -189,8 +485,9 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
189
485
|
|
|
190
486
|
c.context = cctx;
|
|
191
487
|
if (nodeName.getDerivedStateFromProps)
|
|
192
|
-
c.state = assign(
|
|
193
|
-
|
|
488
|
+
c.state = Object.assign(
|
|
489
|
+
{},
|
|
490
|
+
c.state,
|
|
194
491
|
nodeName.getDerivedStateFromProps(c.props, c.state)
|
|
195
492
|
);
|
|
196
493
|
else if (c.componentWillMount) {
|
|
@@ -212,11 +509,11 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
212
509
|
}
|
|
213
510
|
|
|
214
511
|
if (c.getChildContext) {
|
|
215
|
-
context = assign(
|
|
512
|
+
context = Object.assign({}, context, c.getChildContext());
|
|
216
513
|
}
|
|
217
514
|
|
|
218
515
|
if (options.diffed) options.diffed(vnode);
|
|
219
|
-
return
|
|
516
|
+
return _renderToStringPretty(
|
|
220
517
|
rendered,
|
|
221
518
|
context,
|
|
222
519
|
opts,
|
|
@@ -266,7 +563,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
266
563
|
} else if (name === 'className') {
|
|
267
564
|
if (typeof props.class !== 'undefined') continue;
|
|
268
565
|
name = 'class';
|
|
269
|
-
} else if (isSvgMode &&
|
|
566
|
+
} else if (isSvgMode && XLINK.test(name)) {
|
|
270
567
|
name = name.toLowerCase().replace(/^xlink:?/, 'xlink:');
|
|
271
568
|
}
|
|
272
569
|
|
|
@@ -368,7 +665,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
368
665
|
: nodeName === 'foreignObject'
|
|
369
666
|
? false
|
|
370
667
|
: isSvgMode,
|
|
371
|
-
ret =
|
|
668
|
+
ret = _renderToStringPretty(
|
|
372
669
|
child,
|
|
373
670
|
context,
|
|
374
671
|
opts,
|
package/src/util.js
CHANGED
|
@@ -3,16 +3,34 @@ export const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine
|
|
|
3
3
|
|
|
4
4
|
const ENCODED_ENTITIES = /[&<>"]/;
|
|
5
5
|
|
|
6
|
-
export function encodeEntities(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
export function encodeEntities(str) {
|
|
7
|
+
// Ensure we're always parsing and returning a string:
|
|
8
|
+
str += '';
|
|
9
|
+
|
|
10
|
+
// Skip all work for strings with no entities needing encoding:
|
|
11
|
+
if (ENCODED_ENTITIES.test(str) === false) return str;
|
|
12
|
+
|
|
13
|
+
let last = 0,
|
|
14
|
+
i = 0,
|
|
15
|
+
out = '',
|
|
16
|
+
ch = '';
|
|
17
|
+
|
|
18
|
+
// Seek forward in str until the next entity char:
|
|
19
|
+
for (; i<str.length; i++) {
|
|
20
|
+
switch (str.charCodeAt(i)) {
|
|
21
|
+
case 60: ch = '<'; break;
|
|
22
|
+
case 62: ch = '>'; break;
|
|
23
|
+
case 34: ch = '"'; break;
|
|
24
|
+
case 38: ch = '&'; break;
|
|
25
|
+
default: continue;
|
|
26
|
+
}
|
|
27
|
+
// Append skipped/buffered characters and the encoded entity:
|
|
28
|
+
if (i > last) out += str.slice(last, i);
|
|
29
|
+
out += ch;
|
|
30
|
+
// Start the next seek/buffer after the entity's offset:
|
|
31
|
+
last = i + 1;
|
|
10
32
|
}
|
|
11
|
-
return
|
|
12
|
-
.replace(/&/g, '&')
|
|
13
|
-
.replace(/</g, '<')
|
|
14
|
-
.replace(/>/g, '>')
|
|
15
|
-
.replace(/"/g, '"');
|
|
33
|
+
return out + str.slice(last, i);
|
|
16
34
|
}
|
|
17
35
|
|
|
18
36
|
export let indent = (s, char) =>
|
|
@@ -57,8 +75,7 @@ export function styleObjToCss(s) {
|
|
|
57
75
|
* @private
|
|
58
76
|
*/
|
|
59
77
|
export function assign(obj, props) {
|
|
60
|
-
|
|
61
|
-
return obj;
|
|
78
|
+
return Object.assign(obj, props);
|
|
62
79
|
}
|
|
63
80
|
|
|
64
81
|
/**
|