Skip to content

Commit

Permalink
Merge pull request #192 from preactjs/performance-improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
marvinhagemeister authored Mar 25, 2021
2 parents e7fad70 + 95e4bc8 commit d1a9ddd
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 47 deletions.
81 changes: 44 additions & 37 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import {
assign,
getChildren
} from './util';
import { options, Fragment, createElement } from 'preact';
import { options, Fragment } from 'preact';

/** @typedef {import('preact').VNode} VNode */

const SHALLOW = { shallow: true };

Expand All @@ -28,7 +30,7 @@ const noop = () => {};
* @param {Boolean} [options.shallow=false] If `true`, renders nested Components as HTML elements (`<Foo a="b" />`).
* @param {Boolean} [options.xml=false] If `true`, uses self-closing tags for elements without children.
* @param {Boolean} [options.pretty=false] If `true`, adds whitespace for readability
* @param {RegEx|undefined} [options.voidElements] RegeEx that matches elements that are considered void (self-closing)
* @param {RegExp|undefined} [options.voidElements] RegeEx that matches elements that are considered void (self-closing)
*/
renderToString.render = renderToString;

Expand All @@ -43,6 +45,8 @@ let shallowRender = (vnode, context) => renderToString(vnode, context, SHALLOW);

const EMPTY_ARR = [];
function renderToString(vnode, context, opts) {
context = context || {};
opts = opts || {};
const res = _renderToString(vnode, context, opts);
// options._commit, we don't schedule any effects in this library right now,
// so we can pass an empty queue to this hook.
Expand All @@ -56,48 +60,50 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
return '';
}

// wrap array nodes in Fragment
if (Array.isArray(vnode)) {
vnode = createElement(Fragment, null, vnode);
// #text nodes
if (typeof vnode !== 'object') {
return encodeEntities(vnode);
}

let nodeName = vnode.type,
props = vnode.props,
isComponent = false;
context = context || {};
opts = opts || {};

let pretty = opts.pretty,
indentChar = pretty && typeof pretty === 'string' ? pretty : '\t';

// #text nodes
if (typeof vnode !== 'object' && !nodeName) {
return encodeEntities(vnode);
if (Array.isArray(vnode)) {
let rendered = '';
for (let i = 0; i < vnode.length; i++) {
if (pretty && i > 0) rendered += '\n';
rendered += _renderToString(
vnode[i],
context,
opts,
inner,
isSvgMode,
selectValue
);
}
return rendered;
}

let nodeName = vnode.type,
props = vnode.props,
isComponent = false;

// components
if (typeof nodeName === 'function') {
isComponent = true;
if (opts.shallow && (inner || opts.renderRootComponent === false)) {
nodeName = getComponentName(nodeName);
} else if (nodeName === Fragment) {
let rendered = '';
let children = [];
const children = [];
getChildren(children, vnode.props.children);

for (let i = 0; i < children.length; i++) {
rendered +=
(i > 0 && pretty ? '\n' : '') +
_renderToString(
children[i],
context,
opts,
opts.shallowHighOrder !== false,
isSvgMode,
selectValue
);
}
return rendered;
return _renderToString(
children,
context,
opts,
opts.shallowHighOrder !== false,
isSvgMode,
selectValue
);
} else {
let rendered;

Expand Down Expand Up @@ -197,7 +203,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
}

// render JSX to HTML
let s = '',
let s = '<' + nodeName,
propChildren,
html;

Expand All @@ -215,7 +221,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
continue;
}

if (name.match(/[\s\n\\/='"\0<>]/)) continue;
if (UNSAFE_NAME.test(name)) continue;

if (
!(opts && opts.allAttributes) &&
Expand Down Expand Up @@ -287,18 +293,19 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {

// account for >1 multiline attribute
if (pretty) {
let sub = s.replace(/^\n\s*/, ' ');
let sub = s.replace(/\n\s*/, ' ');
if (sub !== s && !~sub.indexOf('\n')) s = sub;
else if (pretty && ~s.indexOf('\n')) s += '\n';
}

s = `<${nodeName}${s}>`;
if (UNSAFE_NAME.test(String(nodeName)))
s += '>';

if (UNSAFE_NAME.test(nodeName))
throw new Error(`${nodeName} is not a valid HTML tag name in ${s}`);

let isVoid =
VOID_ELEMENTS.test(String(nodeName)) ||
(opts.voidElements && opts.voidElements.test(String(nodeName)));
VOID_ELEMENTS.test(nodeName) ||
(opts.voidElements && opts.voidElements.test(nodeName));
let pieces = [];

let children;
Expand Down
15 changes: 5 additions & 10 deletions src/util.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
// DOM properties that should NOT have "px" added when numeric
export const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|^--/i;

const HTML_ENTITY_REG = /[&<>"]/g;
const tagsToReplace = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;'
};
const replaceTag = (tag) => tagsToReplace[tag] || tag;
export function encodeEntities(s) {
if (typeof s !== 'string') s = String(s);
return s.replace(HTML_ENTITY_REG, replaceTag);
return String(s)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}

export let indent = (s, char) =>
Expand Down

0 comments on commit d1a9ddd

Please sign in to comment.