Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix interleaveWithNodeStream streaming flow to be compatible with React 18 api #4213

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
53 changes: 48 additions & 5 deletions packages/styled-components/src/models/ServerStyleSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import { StyleSheetManager } from './StyleSheetManager';
declare const __SERVER__: boolean;

const CLOSING_TAG_R = /^\s*<\/[a-z]/i;
const OPENING_TAG_R = /<[^/>][^>]*>/;

const hasContentInStyle = (string: string) => {
const num = string.indexOf('</style>');
return string[num - 1] !== '>';
};

export default class ServerStyleSheet {
instance: StyleSheet;
Expand Down Expand Up @@ -88,6 +94,20 @@ export default class ServerStyleSheet {
const readableStream: Readable = input;
const { instance: sheet, _emitSheetCSS } = this;

let queue: string[] = [];

const takeStylesFromQueue = () => {
const styles = queue.join('\n');
queue = [];
return styles;
};

const splitHtmlByIndex = (html: string, splitByIndex: number) => {
const before = html.slice(0, splitByIndex);
const after = html.slice(splitByIndex);
return [before, after];
};

const transformer: streamInternal.Transform = new Transform({
transform: function appendStyleChunks(
chunk: string,
Expand All @@ -102,16 +122,39 @@ export default class ServerStyleSheet {

sheet.clearTag();

// we don't need to insert an empty <style> tag into the response,
// which is related to "_emitSheetCSS" which will return an empty <style> tag
// even if we don't have styles to send
if (
!hasContentInStyle(html) &&
// also we want to check if we need to shift all of our styles
queue.length === 0
) {
this.push(renderedHtml);
callback();
return;
}
// prepend style html to chunk, unless the start of the chunk is a
// closing tag in which case append right after that
if (CLOSING_TAG_R.test(renderedHtml)) {
else if (CLOSING_TAG_R.test(renderedHtml)) {
const endOfClosingTag = renderedHtml.indexOf('>') + 1;
const before = renderedHtml.slice(0, endOfClosingTag);
const after = renderedHtml.slice(endOfClosingTag);
const [before, after] = splitHtmlByIndex(renderedHtml, endOfClosingTag);

queue.push(html);

this.push(before + takeStylesFromQueue() + after);
} else if (OPENING_TAG_R.test(renderedHtml)) {
// check if we have open tags
const startOfStartingTag = renderedHtml.search(OPENING_TAG_R);
const [before, after] = splitHtmlByIndex(renderedHtml, startOfStartingTag);

queue.push(html);

this.push(before + html + after);
this.push(before + takeStylesFromQueue() + after);
} else {
this.push(html + renderedHtml);
// edge case case when we don't have any tags, only content such as an svg path or large text
queue.push(html);
this.push(renderedHtml);
}

callback();
Expand Down