Skip to content

Commit 4957fd5

Browse files
committed
[IMP] components: make initial component sync if possible
1 parent 8f23fcc commit 4957fd5

7 files changed

Lines changed: 67 additions & 52 deletions

File tree

src/runtime/app.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,17 @@ export class App extends TemplateSet {
135135

136136
const fiber = new MountFiber(node, target, options);
137137
this.scheduler.addFiber(fiber);
138-
const prev = getCurrentComputation();
139-
node.initiateRender(fiber);
140-
setComputation(prev);
138+
if (node.willStart.length) {
139+
const prev = getCurrentComputation();
140+
node.initiateRender(fiber);
141+
setComputation(prev);
142+
} else {
143+
node.fiber = fiber;
144+
if (node.mounted.length) {
145+
fiber.root!.mounted.push(fiber);
146+
}
147+
fiber.render();
148+
}
141149
return promise;
142150
},
143151
destroy: () => {

tests/app/sub_root.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ test("destroy a subroot while another component is mounted in main app", async (
114114

115115
const app = new App();
116116
const comp = await app.createRoot(SomeComponent).mount(fixture);
117-
expect(fixture.innerHTML).toBe("a<div></div>");
118117
await nextTick();
119118
expect(fixture.innerHTML).toBe("a<div>c</div>");
120119
comp.state.flag = true;

tests/components/error_handling.test.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -321,11 +321,12 @@ describe("errors and promises", () => {
321321

322322
const app = new App();
323323
let error: OwlError;
324-
const mountProm = app
325-
.createRoot(Root)
324+
const root = app.createRoot(Root);
325+
const errorProm = nextAppError(app);
326+
const mountProm = root
326327
.mount(fixture)
327328
.catch((e: Error) => (error = e));
328-
await expect(nextAppError(app)).resolves.toThrow(
329+
await expect(errorProm).resolves.toThrow(
329330
"[Owl] Unhandled error. Destroying the root component"
330331
);
331332
await mountProm;
@@ -347,11 +348,12 @@ describe("errors and promises", () => {
347348

348349
const app = new App({ test: true });
349350
let error: OwlError;
350-
const mountProm = app
351-
.createRoot(Root)
351+
const root = app.createRoot(Root);
352+
const errorProm = nextAppError(app);
353+
const mountProm = root
352354
.mount(fixture)
353355
.catch((e: Error) => (error = e));
354-
await expect(nextAppError(app)).resolves.toThrow(
356+
await expect(errorProm).resolves.toThrow(
355357
"[Owl] Unhandled error. Destroying the root component"
356358
);
357359
await mountProm;
@@ -375,11 +377,12 @@ describe("errors and promises", () => {
375377

376378
const app = new App({ test: true });
377379
let error: OwlError;
378-
const mountProm = app
379-
.createRoot(Root)
380+
const root = app.createRoot(Root);
381+
const errorProm = nextAppError(app);
382+
const mountProm = root
380383
.mount(fixture)
381384
.catch((e: Error) => (error = e));
382-
await expect(nextAppError(app)).resolves.toThrow(
385+
await expect(errorProm).resolves.toThrow(
383386
"[Owl] Unhandled error. Destroying the root component"
384387
);
385388
await mountProm;

tests/components/props_validation.test.ts

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@ describe("props validation", () => {
4949

5050
const app = new App({ test: true });
5151
let error: OwlError | undefined;
52-
const mountProm = app
53-
.createRoot(Parent)
52+
const root = app.createRoot(Parent);
53+
const errorProm = nextAppError(app);
54+
const mountProm = root
5455
.mount(fixture)
5556
.catch((e: Error) => (error = e));
56-
await expect(nextAppError(app)).resolves.toThrow(
57+
await expect(errorProm).resolves.toThrow(
5758
"[Owl] Unhandled error. Destroying the root component"
5859
);
5960
await mountProm;
@@ -81,11 +82,12 @@ describe("props validation", () => {
8182

8283
const app = new App({ test: true });
8384
let error: OwlError | undefined;
84-
const mountProm = app
85-
.createRoot(Parent)
85+
const root = app.createRoot(Parent);
86+
const errorProm = nextAppError(app);
87+
const mountProm = root
8688
.mount(fixture)
8789
.catch((e: Error) => (error = e));
88-
await expect(nextAppError(app)).resolves.toThrow(
90+
await expect(errorProm).resolves.toThrow(
8991
"[Owl] Unhandled error. Destroying the root component"
9092
);
9193
await mountProm;
@@ -135,11 +137,12 @@ describe("props validation", () => {
135137
state = {};
136138
let app = new App({ test: true });
137139
let error: OwlError | undefined;
138-
let mountProm = app
139-
.createRoot(Parent)
140+
let root = app.createRoot(Parent);
141+
let errorProm = nextAppError(app);
142+
let mountProm = root
140143
.mount(fixture)
141144
.catch((e: Error) => (error = e));
142-
await expect(nextAppError(app)).resolves.toThrow(
145+
await expect(errorProm).resolves.toThrow(
143146
"[Owl] Unhandled error. Destroying the root component"
144147
);
145148
await mountProm;
@@ -155,11 +158,12 @@ describe("props validation", () => {
155158
expect(error!).toBeUndefined();
156159
state = { p: test.ko };
157160
app = new App({ test: true });
158-
mountProm = app
159-
.createRoot(Parent)
161+
root = app.createRoot(Parent);
162+
errorProm = nextAppError(app);
163+
mountProm = root
160164
.mount(fixture)
161165
.catch((e: Error) => (error = e));
162-
await expect(nextAppError(app)).resolves.toThrow(
166+
await expect(errorProm).resolves.toThrow(
163167
"[Owl] Unhandled error. Destroying the root component"
164168
);
165169
await mountProm;
@@ -198,11 +202,12 @@ describe("props validation", () => {
198202
expect(error!).toBeUndefined();
199203
state = { p: 1 };
200204
const app = new App({ test: true });
201-
const mountProm = app
202-
.createRoot(Parent)
205+
const root = app.createRoot(Parent);
206+
const errorProm = nextAppError(app);
207+
const mountProm = root
203208
.mount(fixture)
204209
.catch((e: Error) => (error = e));
205-
await expect(nextAppError(app)).resolves.toThrow(
210+
await expect(errorProm).resolves.toThrow(
206211
"[Owl] Unhandled error. Destroying the root component"
207212
);
208213
await mountProm;
@@ -240,11 +245,12 @@ describe("props validation", () => {
240245
expect(error!).toBeUndefined();
241246
state = { p: 1 };
242247
const app = new App({ test: true });
243-
const mountProm = app
244-
.createRoot(Parent)
248+
const root = app.createRoot(Parent);
249+
const errorProm = nextAppError(app);
250+
const mountProm = root
245251
.mount(fixture)
246252
.catch((e: Error) => (error = e));
247-
await expect(nextAppError(app)).resolves.toThrow(
253+
await expect(errorProm).resolves.toThrow(
248254
"[Owl] Unhandled error. Destroying the root component"
249255
);
250256
await mountProm;
@@ -333,11 +339,12 @@ describe("props validation", () => {
333339
expect(error!).toBeUndefined();
334340
state = { p: [true, 1] };
335341
const app = new App({ test: true });
336-
const mountProm = app
337-
.createRoot(Parent)
342+
const root = app.createRoot(Parent);
343+
const errorProm = nextAppError(app);
344+
const mountProm = root
338345
.mount(fixture)
339346
.catch((e: Error) => (error = e));
340-
await expect(nextAppError(app)).resolves.toThrow(
347+
await expect(errorProm).resolves.toThrow(
341348
"[Owl] Unhandled error. Destroying the root component"
342349
);
343350
await mountProm;

tests/components/reactivity.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,9 @@ describe("reactivity in lifecycle", () => {
185185
const prom = mount(Comp, fixture);
186186
(STATE as any).val = 2;
187187
await prom;
188-
expect(steps).toEqual([2]);
188+
// The first render happens synchronously (fast path, no willStart),
189+
// so the template executes with val=1. Then val=2 triggers a re-render.
190+
expect(steps).toEqual([1, 2]);
189191
expect(fixture.innerHTML).toBe("<div>2</div>");
190192
});
191193

tests/components/refs.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,12 @@ describe("refs", () => {
131131
}
132132

133133
const app = new App({ test: true });
134-
const mountProm = expect(app.createRoot(Test).mount(fixture)).rejects.toThrow(
134+
const root = app.createRoot(Test);
135+
const errorProm = nextAppError(app);
136+
const mountProm = expect(root.mount(fixture)).rejects.toThrow(
135137
'Cannot set the same ref more than once in the same component, ref "coucou" was set multiple times in Test'
136138
);
137-
await expect(nextAppError(app)).resolves.toThrow(
139+
await expect(errorProm).resolves.toThrow(
138140
'Cannot set the same ref more than once in the same component, ref "coucou" was set multiple times in Test'
139141
);
140142
await mountProm;

tests/helpers.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -277,27 +277,21 @@ expect.extend({
277277
});
278278

279279
export function nextAppError(app: any) {
280-
const { _handleError } = app;
281-
const rootPromises = [...app.roots].map((r) => r.promise);
282-
283-
let settled = false;
284-
285-
const done = (error: any, restore = true) => {
286-
if (settled) return;
287-
settled = true;
288-
if (restore) app._handleError = _handleError;
289-
resolve(error);
290-
};
291-
292280
let resolve: (value: any) => void;
293281
const result = new Promise((res) => (resolve = res));
294282

283+
const original = app._handleError;
295284
app._handleError = (error: any) => {
296-
done(error);
285+
app._handleError = original;
286+
resolve(error);
297287
};
298288

299-
for (const p of rootPromises) {
300-
p.catch((err: any) => done(err));
289+
// Also catch rejections from root mount promises
290+
for (const root of app.roots) {
291+
root.promise.catch((err: any) => {
292+
app._handleError = original;
293+
resolve(err);
294+
});
301295
}
302296

303297
return result;

0 commit comments

Comments
 (0)