POC rendering via queue

This commit is contained in:
Emanuele Stoppa 2023-03-21 16:52:49 -03:00
parent 97f876abba
commit b6748f3be9
2 changed files with 188 additions and 0 deletions

View file

@ -0,0 +1,118 @@
/**
* We take a root as function and we start creating a quue
*/
export function orderRoot(root) {
const queue = [root];
let result = [];
let previousNode;
// we apply a do/while because we need to process the root first
do {
// we pop elements from the back
let currentNode = queue.pop();
if (Array.isArray(currentNode.children)) {
// if we have children, we fill the queue
queue.push(...currentNode.children);
const newNode = {};
if (currentNode.node) {
newNode.node = currentNode.node;
}
// we need to create a new data structure were we need to keep track of the parent of each node
// the root doesn't have any parent
if (previousNode) {
newNode.parent = previousNode.node;
}
// track the previous node
previousNode = currentNode;
// fill the new node
result.push(newNode);
} else {
// usually here we have leafs and "children" should be just a string
const newNode = { content: currentNode.children };
if (currentNode.node) {
newNode.node = currentNode.node;
}
if (previousNode) {
newNode.parent = previousNode.node;
}
result.push(newNode);
}
} while (queue.length > 0);
return result.reverse();
}
export function renderQueue(queue) {
let html = '';
let previousParent;
while (queue.length > 0) {
let element = queue.shift();
if (!element.parent) {
html = `<${element.node}>${html}</${element.node}>`;
break;
}
if (!previousParent) {
if (element.node) {
html += renderElement(element.node, element.content);
} else {
html += element.content;
}
previousParent = element.parent;
continue;
}
if (previousParent === element.node) {
if (element.content) {
if (element.node) {
html += renderElement(element.node, element.content);
} else {
html += element.content;
}
} else {
html = `<${element.node}>${html}</${element.node}>`;
}
} else {
let [side, parent] = renderUntilElement(element, queue, element.parent);
previousParent = parent;
html += side;
}
previousParent = element.parent;
}
return html;
}
function renderUntilElement(previousElement, iterator, parent) {
let html = '';
let previousParent;
if (previousElement.content) {
if (previousElement.node) {
html += renderElement(previousElement.node, previousElement.content);
} else {
html += previousElement.content;
}
} else {
html = renderElement(previousElement.node, html);
}
while (iterator.length > 0) {
let element = iterator.shift();
if (element.node === parent) {
html = renderElement(element.node, html);
previousParent = element.node;
break;
} else {
if (element.content) {
if (element.node) {
html += renderElement(element.node, element.content);
} else {
html += element.content;
}
} else {
html = renderElement(element.node, html);
}
}
}
return [html, previousParent];
}
function renderElement(node, content) {
return `<${node}>${content}</${node}>`;
}

View file

@ -15,6 +15,7 @@ import {
} from '../../../dist/core/render/index.js';
import { createAstroJSXComponent, renderer as jsxRenderer } from '../../../dist/jsx/index.js';
import { defaultLogging as logging } from '../../test-utils.js';
import { orderRoot, renderQueue } from './jsx.proto.js';
const createAstroModule = (AstroComponent) => ({ default: AstroComponent });
const loadJSXRenderer = () => loadRenderer(jsxRenderer, (s) => import(s));
@ -29,6 +30,36 @@ describe('core/render', () => {
});
});
it('new rendering order', async () => {
const Page = createAstroJSXComponent(() => {
return jsx('main', {
children: [
jsx('p', {
className: 'n',
children: [
jsx('span', {
children: 'label 1',
}),
' ',
jsx('span', {
children: 'label 2',
}),
],
}),
],
});
});
const ctx = createRenderContext({ request: new Request('http://example.com/') });
const response = await renderPage(createAstroModule(Page), ctx, env);
expect(response.status).to.equal(200);
const html = await response.text();
console.log(html);
expect(html).to.include('<div><p class="n">works</p></div>');
});
it('Can render slots', async () => {
const Wrapper = createComponent((result, _props, slots = {}) => {
return render`<div>${renderSlot(result, slots['myslot'])}</div>`;
@ -117,3 +148,42 @@ describe('core/render', () => {
});
});
});
describe('new engine', () => {
it('orderRoot', () => {
const root = {
node: 'p',
children: [
{ node: 'span', children: [{ node: 'a', children: 'I am a link' }] },
{
node: 'span',
children: [
{ children: 'I am a text' },
{ node: 'strong', children: 'I am strong' },
{ node: 'em', children: 'I am em' },
{ node: 'u', children: 'I am underline' },
],
},
],
};
const expected = [
{ node: 'a', content: 'I am a link', parent: 'span' },
{ node: 'span', parent: 'span' },
{ content: 'I am a text', parent: 'span' },
{ node: 'strong', content: 'I am strong', parent: 'span' },
{ node: 'em', content: 'I am em', parent: 'span' },
{ node: 'u', content: 'I am underline', parent: 'span' },
{ node: 'span', parent: 'p' },
{ node: 'p' },
];
const expectedString = `<p><span><a>I am a link</a></span><span>I am a text<strong>I am strong</strong><em>I am em</em><u>I am underline</u></span></p>`;
let result = orderRoot(root);
expect(result).to.deep.equal(expected);
let rendered = renderQueue(result);
expect(rendered).to.deep.equal(expectedString);
});
});