Skip to content

Commit aaa6cda

Browse files
authored
fix corner cases & public api update (#192)
* fix corner cases fix corner cases * update renderComponent to match ember rfc
1 parent 9a4db6a commit aaa6cda

13 files changed

+69
-126
lines changed

README.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,12 @@ import App from "./App.gts";
208208
const Instance = renderComponent(
209209
App, {
210210
// application arguments
211-
name: 'My App'
212-
},
213-
document.getElementById("app"),
211+
args: {
212+
name: 'My App'
213+
},
214+
// render target (append to)
215+
element: document.getElementById("app"),
216+
}
214217
);
215218
```
216219

src/tests/integration/multiroot-test.gts

+9-3
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,16 @@ module('Integration | multiroot', function () {
2929
assert.dom(r2).exists('app two node exists');
3030
assert.ok(r3, 'app three node exists');
3131

32-
const appOneInstance = renderComponent(AppOne, {}, r1);
33-
const appTwoInstance = renderComponent(AppOne, {}, r2);
32+
const appOneInstance = renderComponent(AppOne, {
33+
element: r1,
34+
});
35+
const appTwoInstance = renderComponent(AppOne, {
36+
element: r2,
37+
});
3438

35-
const appThreeInstance = renderComponent(AppOne, {}, r3);
39+
const appThreeInstance = renderComponent(AppOne, {
40+
element: r3,
41+
});
3642

3743
function qButton(r: Element) {
3844
return r.querySelector('[data-test-button]') as HTMLButtonElement;

src/tests/utils.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,12 @@ export async function render(component: ComponentReturnType) {
116116
let renderResult = renderComponent(
117117
createTestComponent(component, owner),
118118
{
119-
[$context]: owner,
120-
},
121-
targetElement,
122-
owner,
123-
false,
119+
args: {
120+
[$context]: owner,
121+
},
122+
element: targetElement,
123+
owner,
124+
}
124125
);
125126
await rerender();
126127
return renderResult;

src/utils/benchmark/benchmark.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@ export function createBenchmark(doc: Document) {
1919
console.error('Rehydration failed, fallback to normal render', e);
2020
const fragment = doc.createDocumentFragment();
2121
cleanupFastContext();
22-
renderComponent(Application, {}, fragment, new Root(doc));
22+
renderComponent(Application, {
23+
element: fragment,
24+
owner: new Root(doc),
25+
});
2326
root.innerHTML = '';
2427
root.appendChild(fragment);
2528
}
2629
} else {
27-
renderComponent(Application, {}, root);
30+
renderComponent(Application, {
31+
element: root,
32+
});
2833
}
2934
});
3035

src/utils/component.ts

+23-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
destroy,
33
registerDestructor,
4-
Destructors,
54
destroySync,
65
} from '@/utils/glimmer/destroyable';
76
import type {
@@ -36,7 +35,8 @@ import {
3635
RENDERING_CONTEXT,
3736
cleanupFastContext,
3837
} from './context';
39-
import { cellToText, createRoot, MergedCell } from '.';
38+
import { createRoot, MergedCell } from '.';
39+
import { opcodeFor } from './vm';
4040

4141
export type ComponentRenderTarget =
4242
| Element
@@ -91,13 +91,14 @@ export function renderElement(
9191
// el.ctx![RENDERED_NODES_PROPERTY].reverse();
9292
} else if (isFn(el)) {
9393
// @ts-expect-error
94-
renderElement(api, ctx, target, resolveRenderable(el), placeholder);
94+
renderElement(api, ctx, target, resolveRenderable(el), placeholder, skipRegistration);
9595
} else if (isTagLike(el)) {
96-
const destructors: Destructors = [];
97-
const node = cellToText(api, el, destructors);
96+
const node = api.text('');
9897
ctx[RENDERED_NODES_PROPERTY].push(node);
9998
api.insert(target, node, placeholder);
100-
registerDestructor(ctx, ...destructors);
99+
registerDestructor(ctx, opcodeFor(el, (value) => {
100+
api.textContent(node, String(value ?? ''));
101+
}));
101102
} else {
102103
throw new Error(`Unknown element type ${el}`);
103104
}
@@ -110,11 +111,16 @@ export function renderElement(
110111

111112
export function renderComponent(
112113
component: typeof Component<any>,
113-
componentArgs: Record<string, unknown>,
114-
target: ComponentRenderTarget,
115-
appRoot: Root | Component<any> = createRoot(),
116-
skipRoot?: boolean,
114+
params: {
115+
owner?: Root;
116+
args?: Record<string, unknown>;
117+
element?: ComponentRenderTarget;
118+
} = {},
117119
): ComponentReturnType {
120+
const appRoot = params.owner ?? createRoot();
121+
const target = params.element ?? document.body;
122+
const componentArgs = params.args ?? {};
123+
118124
if (import.meta.env.DEV) {
119125
if (target === undefined) {
120126
throw new Error(`Trying to render undefined`);
@@ -123,15 +129,13 @@ export function renderComponent(
123129
cleanupFastContext();
124130
const targetElement = targetFor(target);
125131

126-
if (!skipRoot) {
127-
if (!initDOM(appRoot)) {
128-
// setting default dom api
129-
provideContext(
130-
appRoot,
131-
RENDERING_CONTEXT,
132-
new HTMLBrowserDOMApi((appRoot as Root).document),
133-
);
134-
}
132+
if (!initDOM(appRoot)) {
133+
// setting default dom api
134+
provideContext(
135+
appRoot,
136+
RENDERING_CONTEXT,
137+
new HTMLBrowserDOMApi((appRoot as Root).document),
138+
);
135139
}
136140

137141
const args = {

src/utils/control-flow/list.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,8 @@ export class SyncListComponent<
407407
destroyElementSync(value, true);
408408
}
409409
parent.innerHTML = '';
410-
parent.append(topMarker);
411-
parent.append(bottomMarker);
410+
this.api.insert(parent, topMarker);
411+
this.api.insert(parent, bottomMarker);
412412
keyMap.clear();
413413
indexMap.clear();
414414
return true;
@@ -504,8 +504,8 @@ export class AsyncListComponent<
504504
await Promise.all(promises);
505505
promises.length = 0;
506506
parent.innerHTML = '';
507-
parent.append(topMarker);
508-
parent.append(bottomMarker);
507+
this.api.insert(parent, topMarker);
508+
this.api.insert(parent, bottomMarker);
509509
keyMap.clear();
510510
indexMap.clear();
511511
return true;

src/utils/dom-api.ts

-14
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ export abstract class DOMApi {
1717
abstract textContent(node: Node, text: string): void;
1818
abstract fragment(): DocumentFragment;
1919
abstract element(tagName: string): Node;
20-
abstract append(
21-
parent: HTMLElement | Node,
22-
child: HTMLElement | Node,
23-
// @ts-ignore
24-
targetIndex: number = 0,
25-
): void;
2620
abstract insert(
2721
parent: HTMLElement | Node,
2822
child: HTMLElement | Node,
@@ -84,14 +78,6 @@ export class HTMLBrowserDOMApi implements DOMApi {
8478
element(tagName = ''): HTMLElement {
8579
return this.doc.createElement(tagName);
8680
}
87-
append(
88-
parent: HTMLElement | Node,
89-
child: HTMLElement | Node,
90-
// @ts-ignore
91-
targetIndex: number = 0,
92-
) {
93-
this.insert(parent, child, null);
94-
}
9581
insert(
9682
parent: HTMLElement | Node,
9783
child: HTMLElement | Node,

src/utils/dom.ts

+1-29
Original file line numberDiff line numberDiff line change
@@ -613,35 +613,20 @@ function _DOM(
613613
if (hasShadowMode) {
614614
const tpl = api.element('template');
615615
api.attr(tpl, 'shadowrootmode', 'open');
616-
element.appendChild(tpl);
616+
api.insert(element, tpl);
617617
// @ts-expect-error children type mismatch
618618
renderElement(api, ctx, tpl, children);
619-
// children.forEach((child, index) => {
620-
// addChild(api, tpl, child, destructors, index);
621-
// });
622619
} else {
623620
// @ts-expect-error children type mismatch
624621
renderElement(api, ctx, appendRef!, children);
625-
626-
// children.forEach((child, index) => {
627-
// addChild(api, appendRef!, child, destructors, index);
628-
// });
629622
}
630623
} else {
631624
// @ts-expect-error children type mismatch
632625
renderElement(api, ctx, appendRef!, children);
633-
634-
// children.forEach((child, index) => {
635-
// addChild(api, appendRef!, child, destructors, index);
636-
// });
637626
}
638627
} else {
639628
// @ts-expect-error children type mismatch
640629
renderElement(api, ctx, element, children);
641-
642-
// for (let i = 0; i < children.length; i++) {
643-
// addChild(api, element, children[i], destructors, i);
644-
// }
645630
}
646631

647632
if (destructors.length) {
@@ -1067,19 +1052,6 @@ function slot(name: string, params: () => unknown[], $slot: Slots, ctx: any) {
10671052
}
10681053
return createSlot($slot[name], params, name, ctx);
10691054
}
1070-
export function cellToText(
1071-
api: DOMApi,
1072-
cell: Cell | MergedCell,
1073-
destructors: Destructors,
1074-
) {
1075-
const textNode = api.text('');
1076-
destructors.push(
1077-
opcodeFor(cell, (value) => {
1078-
api.textContent(textNode, String(value ?? ''));
1079-
}),
1080-
);
1081-
return textNode;
1082-
}
10831055

10841056
function getRenderTargets(api: DOMApi, debugName: string) {
10851057
const ifPlaceholder = IS_DEV_MODE ? api.comment(debugName) : api.comment('');

src/utils/math-api.ts

-3
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@ export class MathMLBrowserDOMApi implements DOMApi {
3030
prop(element: SVGElement, name: string, value: string) {
3131
element.setAttribute(name, value);
3232
}
33-
append(parent: SVGElement, child: SVGElement) {
34-
parent.appendChild(child);
35-
}
3633
insert(parent: SVGElement, child: SVGElement) {
3734
parent.insertBefore(child, null);
3835
}

src/utils/ssr/rehydration-dom-api.ts

-36
Original file line numberDiff line numberDiff line change
@@ -156,42 +156,6 @@ export class HTMLRehydrationBrowserDOMApi implements DOMApi {
156156
}
157157
return this.doc.createElement(tagName);
158158
}
159-
append(
160-
parent: HTMLElement | Node,
161-
child: HTMLElement | Node,
162-
targetIndex: number = 0,
163-
) {
164-
if (isRehydrationScheduled()) {
165-
if (import.meta.env.DEV) {
166-
if (!parent) {
167-
debugger;
168-
}
169-
}
170-
// in this case likely child is a text node, and we don't need to append it, we need to prepend it
171-
const childNodes = Array.from(parent.childNodes);
172-
const maybeIndex = childNodes.indexOf(child as any);
173-
if (maybeIndex !== -1 && maybeIndex === targetIndex) {
174-
return;
175-
}
176-
if (childNodes.length === 0) {
177-
this.insert(parent, child, null);
178-
return;
179-
} else if (targetIndex === 0) {
180-
this.insert(parent, child, parent.firstChild);
181-
return;
182-
} else if (targetIndex >= childNodes.length) {
183-
this.insert(parent, child, null);
184-
return;
185-
}
186-
if (!childNodes[targetIndex]) {
187-
throw new Error(`Rehydration filed. Unable to find target node.`);
188-
}
189-
this.insert(parent, child, childNodes[targetIndex]!);
190-
return;
191-
} else {
192-
this.insert(parent, child, null);
193-
}
194-
}
195159
insert(
196160
parent: HTMLElement | Node,
197161
child: HTMLElement | Node,

src/utils/ssr/rehydration.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ export function withRehydration(
141141
}
142142
});
143143

144-
renderComponent(componentCreationCallback, args, targetNode, root, true);
144+
renderComponent(componentCreationCallback, {
145+
args, element: targetNode, owner: root,
146+
});
145147
if (withRehydrationStack.length > 0) {
146148
const lastNodes = Array.from(withRehydrationStack);
147149
console.warn('withRehydrationStack is not empty', lastNodes);

src/utils/ssr/ssr.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ export async function renderInBrowser(
2020
// @todo - add destructor
2121
renderComponent(
2222
componentRenderFn,
23-
args,
24-
rootNode,
25-
root,
23+
{
24+
args,
25+
element: rootNode,
26+
owner: root,
27+
},
2628
);
2729
const html = rootNode.innerHTML;
2830
rootNode.remove();
@@ -44,7 +46,11 @@ export async function render(
4446
doc.body.appendChild(rootNode);
4547

4648
resetNodeCounter();
47-
renderComponent(component, args, rootNode as unknown as HTMLElement, root);
49+
renderComponent(component, {
50+
args,
51+
element: rootNode as unknown as HTMLElement,
52+
owner: root,
53+
});
4854
resetNodeCounter();
4955

5056
const s = new XMLSerializer();

src/utils/svg-api.ts

-3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ export class SVGBrowserDOMApi implements DOMApi {
4242
element.setAttribute(name, value);
4343
}
4444
}
45-
append(parent: SVGElement, child: SVGElement) {
46-
parent.appendChild(child);
47-
}
4845
insert(parent: SVGElement, child: SVGElement) {
4946
parent.insertBefore(child, null);
5047
}

0 commit comments

Comments
 (0)