From 70d5bd5db7fd2157eae2b27a4e9f68b9c1bd956f Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Tue, 21 Mar 2023 16:52:49 -0300 Subject: [PATCH] POC rendering via queue --- packages/astro/test/units/render/jsx.proto.js | 118 ++++++++++++++++++ packages/astro/test/units/render/jsx.test.js | 70 +++++++++++ 2 files changed, 188 insertions(+) create mode 100644 packages/astro/test/units/render/jsx.proto.js diff --git a/packages/astro/test/units/render/jsx.proto.js b/packages/astro/test/units/render/jsx.proto.js new file mode 100644 index 0000000000..2333c369cc --- /dev/null +++ b/packages/astro/test/units/render/jsx.proto.js @@ -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}`; + 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}`; + } + } 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}`; +} diff --git a/packages/astro/test/units/render/jsx.test.js b/packages/astro/test/units/render/jsx.test.js index a42b819cea..90c6bf6046 100644 --- a/packages/astro/test/units/render/jsx.test.js +++ b/packages/astro/test/units/render/jsx.test.js @@ -11,6 +11,7 @@ import { renderSlot, } from '../../../dist/runtime/server/index.js'; import { createBasicPipeline } from '../test-utils.js'; +import { orderRoot, renderQueue } from './jsx.proto.js'; const createAstroModule = (AstroComponent) => ({ default: AstroComponent }); const loadJSXRenderer = () => loadRenderer(jsxRenderer, { import: (s) => import(s) }); @@ -25,6 +26,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('

works

'); + }); + it('Can render slots', async () => { const Wrapper = createComponent((result, _props, slots = {}) => { return render`
${renderSlot(result, slots['myslot'])}
`; @@ -140,3 +171,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 = `

I am a linkI am a textI am strongI am emI am underline

`; + let result = orderRoot(root); + + expect(result).to.deep.equal(expected); + + let rendered = renderQueue(result); + + expect(rendered).to.deep.equal(expectedString); + }); +});