Skip to content

Commit

Permalink
FIX: Addressed component preservation failure during streaming.
Browse files Browse the repository at this point in the history
  • Loading branch information
SolomonLeon authored Feb 1, 2025
1 parent 5fdf499 commit 3f2ccd9
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 14 deletions.
29 changes: 23 additions & 6 deletions src/ReactParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class ReactParser {
}

parse(tokens: Token[]): ReactNode[] {
return tokens.map((token) => {
this.renderer.elIdList.push(0);
const result = tokens.map((token) => {
switch (token.type) {
case 'space': {
return null;
Expand All @@ -33,7 +34,9 @@ class ReactParser {

case 'text': {
const textToken = token as Tokens.Text;
return textToken.tokens ? this.parseInline(textToken.tokens) : token.text;
return textToken.tokens
? this.parseInline(textToken.tokens)
: token.text;
}

case 'blockquote': {
Expand All @@ -49,15 +52,21 @@ class ReactParser {
const listItemChildren = [];

if (item.task) {
listItemChildren.push(this.renderer.checkbox(item.checked ?? false));
listItemChildren.push(
this.renderer.checkbox(item.checked ?? false)
);
}

listItemChildren.push(this.parse(item.tokens));

return this.renderer.listItem(listItemChildren);
});

return this.renderer.list(children, token.ordered, token.ordered ? token.start : undefined);
return this.renderer.list(
children,
token.ordered,
token.ordered ? token.start : undefined
);
}

case 'code': {
Expand All @@ -71,7 +80,10 @@ class ReactParser {
case 'table': {
const tableToken = token as Tokens.Table;
const headerCells = tableToken.header.map((cell, index) => {
return this.renderer.tableCell(this.parseInline(cell.tokens), { header: true, align: token.align[index] });
return this.renderer.tableCell(this.parseInline(cell.tokens), {
header: true,
align: token.align[index],
});
});

const headerRow = this.renderer.tableRow(headerCells);
Expand Down Expand Up @@ -103,10 +115,13 @@ class ReactParser {
}
}
});
this.renderer.elIdList.pop();
return result;
}

parseInline(tokens: Token[] = []): ReactNode[] {
return tokens.map((token) => {
this.renderer.elIdList.push(0);
const result = tokens.map((token) => {
switch (token.type) {
case 'text': {
return this.renderer.text(unescape(token.text));
Expand Down Expand Up @@ -154,6 +169,8 @@ class ReactParser {
}
}
});
this.renderer.elIdList.pop();
return result;
}
}

Expand Down
35 changes: 27 additions & 8 deletions src/ReactRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface ReactRendererOptions {
}

class ReactRenderer {
#elId = 0;
elIdList: number[] = [];
#options: ReactRendererOptions;

constructor(options: ReactRendererOptions = {}) {
Expand All @@ -32,21 +32,32 @@ class ReactRenderer {
const rendererName = key as keyof ReactRenderer;
const rendererFunction = value;

if (!this[rendererName] || rendererName === 'elementId' || typeof rendererFunction !== 'function') {
if (
!this[rendererName] ||
rendererName === 'elementId' ||
rendererName === 'elIdList' ||
typeof rendererFunction !== 'function'
) {
return;
}

const originalFunction = this[rendererName];

this[rendererName] = <T extends typeof originalFunction>(...args: Parameters<T>) => {
this[rendererName] = <T extends typeof originalFunction>(
...args: Parameters<T>
) => {
this.#incrementElId();
return rendererFunction.apply(this, args);
};
});
}
}

#h<T extends ElementType>(el: T, children: ReactNode = null, props = {}): ReactElement {
#h<T extends ElementType>(
el: T,
children: ReactNode = null,
props = {}
): ReactElement {
const elProps = {
key: `marked-react-${this.elementId}`,
};
Expand All @@ -56,11 +67,11 @@ class ReactRenderer {
}

#incrementElId() {
this.#elId += 1;
this.elIdList[this.elIdList.length - 1] += 1;
}

get elementId() {
return this.#elId;
return this.elIdList.join('-');
}

heading(children: ReactNode, level: HeadingLevels) {
Expand Down Expand Up @@ -96,15 +107,23 @@ class ReactRenderer {
}

list(children: ReactNode, ordered: boolean, start: number | undefined) {
return this.#h(ordered ? 'ol' : 'ul', children, ordered && start !== 1 ? { start } : {});
return this.#h(
ordered ? 'ol' : 'ul',
children,
ordered && start !== 1 ? { start } : {}
);
}

listItem(children: ReactNode[]) {
return this.#h('li', children);
}

checkbox(checked: ReactNode) {
return this.#h('input', null, { type: 'checkbox', disabled: true, checked });
return this.#h('input', null, {
type: 'checkbox',
disabled: true,
checked,
});
}

table(children: ReactNode[]) {
Expand Down

0 comments on commit 3f2ccd9

Please sign in to comment.