Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 58 additions & 87 deletions packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -1180,15 +1180,13 @@ function pushViewTransitionAttributes(
}
}

const styleNameCache: Map<string, PrecomputedChunk> = new Map();
function processStyleName(styleName: string): PrecomputedChunk {
const chunk = styleNameCache.get(styleName);
if (chunk !== undefined) {
return chunk;
}
const result = stringToPrecomputedChunk(
escapeTextForBrowser(hyphenateStyleName(styleName)),
);
const styleNameCache: Map<string, string> = new Map();
function processStyleName(styleName: string): string {
const cached = styleNameCache.get(styleName);
if (cached !== undefined) {
return cached;
}
const result = escapeTextForBrowser(hyphenateStyleName(styleName));
styleNameCache.set(styleName, result);
return result;
}
Expand All @@ -1209,7 +1207,7 @@ function pushStyleAttribute(
);
}

let isFirst = true;
let styleString = '';
for (const styleName in style) {
if (!hasOwnProperty.call(style, styleName)) {
continue;
Expand All @@ -1231,48 +1229,42 @@ function pushStyleAttribute(
continue;
}

let nameChunk;
let valueChunk;
let nameStr;
let valueStr;
const isCustomProperty = styleName.indexOf('--') === 0;
if (isCustomProperty) {
nameChunk = stringToChunk(escapeTextForBrowser(styleName));
nameStr = escapeTextForBrowser(styleName);
if (__DEV__) {
checkCSSPropertyStringCoercion(styleValue, styleName);
}
valueChunk = stringToChunk(
escapeTextForBrowser(('' + styleValue).trim()),
);
valueStr = escapeTextForBrowser(('' + styleValue).trim());
} else {
if (__DEV__) {
warnValidStyle(styleName, styleValue);
}

nameChunk = processStyleName(styleName);
nameStr = processStyleName(styleName);
if (typeof styleValue === 'number') {
if (styleValue !== 0 && !isUnitlessNumber(styleName)) {
valueChunk = stringToChunk(styleValue + 'px'); // Presumes implicit 'px' suffix for unitless numbers
valueStr = styleValue + 'px'; // Presumes implicit 'px' suffix for unitless numbers
} else {
valueChunk = stringToChunk('' + styleValue);
valueStr = '' + styleValue;
}
} else {
if (__DEV__) {
checkCSSPropertyStringCoercion(styleValue, styleName);
}
valueChunk = stringToChunk(
escapeTextForBrowser(('' + styleValue).trim()),
);
valueStr = escapeTextForBrowser(('' + styleValue).trim());
}
}
if (isFirst) {
isFirst = false;
// If it's first, we don't need any separators prefixed.
target.push(styleAttributeStart, nameChunk, styleAssign, valueChunk);
if (styleString === '') {
styleString = nameStr + ':' + valueStr;
} else {
target.push(styleSeparator, nameChunk, styleAssign, valueChunk);
styleString += ';' + nameStr + ':' + valueStr;
}
}
if (!isFirst) {
target.push(attributeEnd);
if (styleString !== '') {
target.push(stringToChunk(' style="' + styleString + '"'));
}
}

Expand All @@ -1287,7 +1279,7 @@ function pushBooleanAttribute(
value: string | boolean | number | Function | Object, // not null or undefined
): void {
if (value && typeof value !== 'function' && typeof value !== 'symbol') {
target.push(attributeSeparator, stringToChunk(name), attributeEmptyString);
target.push(stringToChunk(' ' + name + '=""'));
}
}

Expand All @@ -1302,11 +1294,7 @@ function pushStringAttribute(
typeof value !== 'boolean'
) {
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
stringToChunk(' ' + name + '="' + escapeTextForBrowser(value) + '"'),
);
}
}
Expand Down Expand Up @@ -1610,11 +1598,9 @@ function pushAttribute(
}
const sanitizedValue = sanitizeURL('' + value);
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(sanitizedValue)),
attributeEnd,
stringToChunk(
' ' + name + '="' + escapeTextForBrowser(sanitizedValue) + '"',
),
);
return;
}
Expand Down Expand Up @@ -1645,11 +1631,9 @@ function pushAttribute(
}
const sanitizedValue = sanitizeURL('' + value);
target.push(
attributeSeparator,
stringToChunk('xlink:href'),
attributeAssign,
stringToChunk(escapeTextForBrowser(sanitizedValue)),
attributeEnd,
stringToChunk(
' xlink:href="' + escapeTextForBrowser(sanitizedValue) + '"',
),
);
return;
}
Expand All @@ -1667,11 +1651,9 @@ function pushAttribute(
// these aren't boolean attributes (they are coerced to strings).
if (typeof value !== 'function' && typeof value !== 'symbol') {
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
stringToChunk(
' ' + name + '="' + escapeTextForBrowser(value) + '"',
),
);
}
return;
Expand Down Expand Up @@ -1727,20 +1709,14 @@ function pushAttribute(
case 'download': {
// Overloaded Boolean
if (value === true) {
target.push(
attributeSeparator,
stringToChunk(name),
attributeEmptyString,
);
target.push(stringToChunk(' ' + name + '=""'));
} else if (value === false) {
// Ignored
} else if (typeof value !== 'function' && typeof value !== 'symbol') {
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
stringToChunk(
' ' + name + '="' + escapeTextForBrowser(value) + '"',
),
);
}
return;
Expand All @@ -1757,11 +1733,9 @@ function pushAttribute(
(value: any) >= 1
) {
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
stringToChunk(
' ' + name + '="' + escapeTextForBrowser(value) + '"',
),
);
}
return;
Expand All @@ -1775,11 +1749,9 @@ function pushAttribute(
!isNaN(value)
) {
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
stringToChunk(
' ' + name + '="' + escapeTextForBrowser(value) + '"',
),
);
}
return;
Expand Down Expand Up @@ -1837,11 +1809,13 @@ function pushAttribute(
}
}
target.push(
attributeSeparator,
stringToChunk(attributeName),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
stringToChunk(
' ' +
attributeName +
'="' +
escapeTextForBrowser(value) +
'"',
),
);
}
}
Expand Down Expand Up @@ -1956,14 +1930,12 @@ function pushStartAnchor(

pushViewTransitionAttributes(target, formatContext);

target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
if (typeof children === 'string') {
// Special case children as a string to avoid the unnecessary comment.
// TODO: Remove this special case after the general optimization is in place.
target.push(stringToChunk(encodeHTMLTextNode(children)));
if (innerHTML == null && typeof children === 'string') {
target.push(stringToChunk('>' + encodeHTMLTextNode(children)));
return null;
}
target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
return children;
}

Expand Down Expand Up @@ -3964,14 +3936,13 @@ function pushStartGenericElement(

pushViewTransitionAttributes(target, formatContext);

target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
if (typeof children === 'string') {
// Special case children as a string to avoid the unnecessary comment.
// TODO: Remove this special case after the general optimization is in place.
target.push(stringToChunk(encodeHTMLTextNode(children)));
if (innerHTML == null && typeof children === 'string') {
// Fast path: close tag and emit text child in a single push.
target.push(stringToChunk('>' + encodeHTMLTextNode(children)));
return null;
}
target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
return children;
}

Expand Down
7 changes: 5 additions & 2 deletions packages/react-server/src/ReactServerStreamConfigNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,11 @@ export function typedArrayToBinaryChunk(

export function byteLengthOfChunk(chunk: Chunk | PrecomputedChunk): number {
return typeof chunk === 'string'
? Buffer.byteLength(chunk, 'utf8')
: chunk.byteLength;
? chunk.length // Fast path: .length === byte length for ASCII (99%+ of HTML output).
: // For multi-byte chars this slightly underestimates, which is fine
// because byteSize is only used for heuristic decisions (outlining
// threshold and progressive chunk sizing).
chunk.byteLength;
}

export function byteLengthOfBinaryChunk(chunk: BinaryChunk): number {
Expand Down