mikuru 1.0.23 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +11 -10
  3. package/dist/compiler/compatDiagnostics.d.ts +8 -0
  4. package/dist/compiler/compatDiagnostics.js +45 -0
  5. package/dist/compiler/compatDiagnostics.js.map +1 -0
  6. package/dist/compiler/compile.js +29 -16
  7. package/dist/compiler/compile.js.map +1 -1
  8. package/dist/compiler/compileHydration.js +31 -18
  9. package/dist/compiler/compileHydration.js.map +1 -1
  10. package/dist/compiler/compileSsr.js +27 -14
  11. package/dist/compiler/compileSsr.js.map +1 -1
  12. package/dist/compiler/generate.d.ts +17 -1
  13. package/dist/compiler/generate.js +160 -8
  14. package/dist/compiler/generate.js.map +1 -1
  15. package/dist/compiler/generateHydration.js +185 -30
  16. package/dist/compiler/generateHydration.js.map +1 -1
  17. package/dist/compiler/generateSsr.js +14 -4
  18. package/dist/compiler/generateSsr.js.map +1 -1
  19. package/dist/compiler/parseTemplate.js +18 -5
  20. package/dist/compiler/parseTemplate.js.map +1 -1
  21. package/dist/compiler/types.d.ts +1 -0
  22. package/dist/index.d.ts +2 -2
  23. package/dist/index.js +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/router/index.js +27 -3
  26. package/dist/router/index.js.map +1 -1
  27. package/dist/runtime/asyncComponent.js +24 -4
  28. package/dist/runtime/asyncComponent.js.map +1 -1
  29. package/dist/runtime/devtools.d.ts +16 -0
  30. package/dist/runtime/devtools.js +21 -0
  31. package/dist/runtime/devtools.js.map +1 -1
  32. package/dist/runtime/index.d.ts +2 -2
  33. package/dist/runtime/index.js +1 -1
  34. package/dist/runtime/index.js.map +1 -1
  35. package/dist/server.js +16 -6
  36. package/dist/server.js.map +1 -1
  37. package/package.json +1 -1
  38. package/templates/basic/src/App.mikuru +1 -1
@@ -1,4 +1,5 @@
1
1
  import { createCompileError } from "./errors.js";
2
+ import { createScopeAttr, generateChildren as generateDomChildren, generateNode as generateDomNode, withoutForAttrs as withoutDomForAttrs } from "./generate.js";
2
3
  import { compileTemplateExpression, parseForExpression, validateAssignableExpression } from "./parseExpression.js";
3
4
  export function generateHydration(descriptor, root, options = {}) {
4
5
  const context = {
@@ -8,12 +9,13 @@ export function generateHydration(descriptor, root, options = {}) {
8
9
  source: descriptor.source,
9
10
  filename: descriptor.filename
10
11
  };
12
+ context.scopeAttr = descriptor.styleScoped ? createScopeAttr(descriptor) : undefined;
11
13
  const script = splitScript(descriptor.script ?? "");
12
14
  if (options.includeImports !== false) {
13
15
  for (const importLine of script.imports) {
14
16
  emit(context, 0, importLine);
15
17
  }
16
- emit(context, 0, "import { effect, setAttribute, unwrap } from \"mikuru/runtime\";");
18
+ emit(context, 0, "import { effect, ref, setAttribute, unwrap } from \"mikuru/runtime\";");
17
19
  emit(context, 0, "");
18
20
  }
19
21
  emit(context, 0, "export function hydrate(target, props = {}) {");
@@ -22,7 +24,15 @@ export function generateHydration(descriptor, root, options = {}) {
22
24
  emit(context, 1, "const __mikuru_afterUnmount = [];");
23
25
  emit(context, 1, "const __mikuru_mounted = [];");
24
26
  emit(context, 1, "const __mikuru_context = { parent: props.__mikuru_context, provides: new Map(), errorHandler: props.__mikuru_context?.errorHandler, ...__mikuru_componentInfo };");
25
- emit(context, 1, "const __mikuru_try = (fn) => { try { return fn(); } catch (error) { setTimeout(() => { throw error; }); } };");
27
+ emit(context, 1, "const __mikuru_errorInfo = (phase) => ({ ...__mikuru_componentInfo, phase });");
28
+ emit(context, 1, "const __mikuru_reportError = (error, errorHandler = __mikuru_context.errorHandler, phase = \"runtime\") => { if (typeof errorHandler === \"function\") { Promise.resolve().then(() => errorHandler(error, __mikuru_errorInfo(phase))); return; } setTimeout(() => { throw error; }); };");
29
+ emit(context, 1, "const __mikuru_try = (fn, errorHandler, phase) => { try { return fn(); } catch (error) { __mikuru_reportError(error, errorHandler, phase); } };");
30
+ emit(context, 1, "const __mikuru_memoEqual = (previous, next) => Array.isArray(previous) && Array.isArray(next) && previous.length === next.length && previous.every((value, index) => Object.is(value, next[index]));");
31
+ emit(context, 1, "const __mikuru_guardEventHandler = (fn, errorHandler = __mikuru_context.errorHandler) => (...args) => __mikuru_try(() => fn(...args), errorHandler, \"event\");");
32
+ emit(context, 1, "const __mikuru_runCleanup = (cleanups) => { for (const cleanup of cleanups.splice(0).reverse()) __mikuru_try(cleanup, undefined, \"cleanup\"); };");
33
+ emit(context, 1, "const __mikuru_removeNode = (node) => { if (node?.parentNode) node.remove(); };");
34
+ emit(context, 1, "const __mikuru_applyTransitionEnter = () => {};");
35
+ emit(context, 1, "const __mikuru_applyTransitionMove = () => {};");
26
36
  emit(context, 1, "const __mikuru_previousRegistrar = globalThis.__mikuru_currentRegistrar;");
27
37
  emit(context, 1, "globalThis.__mikuru_currentRegistrar = {");
28
38
  emit(context, 2, "registerMounted: (fn) => __mikuru_mounted.push(fn),");
@@ -40,8 +50,8 @@ export function generateHydration(descriptor, root, options = {}) {
40
50
  emit(context, 1, "const __mikuru_emitDebug = (type, payload) => { const hook = globalThis.__MIKURU_DEVTOOLS__; if (!hook) return; const event = { type, timestamp: Date.now(), payload }; hook.events ??= []; hook.events.push(event); if (typeof hook.emit === \"function\") hook.emit(event); for (const listener of hook.listeners ?? []) { try { listener(event); } catch (error) { setTimeout(() => { throw error; }); } } };");
41
51
  emit(context, 1, "const __mikuru_domPath = (node) => { if (!node) return \"missing\"; const parts = []; for (let cursor = node.nodeType === 1 ? node : node.parentElement; cursor && cursor.nodeType === 1; cursor = cursor.parentElement) { const tag = cursor.tagName.toLowerCase(); const siblings = cursor.parentElement ? Array.from(cursor.parentElement.children).filter((child) => child.tagName === cursor.tagName) : []; const index = siblings.length > 1 ? `:nth-of-type(${siblings.indexOf(cursor) + 1})` : \"\"; parts.unshift(`${tag}${index}`); } return parts.join(\" > \") || __mikuru_describeNode(node); };");
42
52
  emit(context, 1, "const __mikuru_inferDiagnostic = (message) => { const action = message.includes(\"falling back to mount\") ? \"mount-fallback\" : message.includes(\"remounting\") ? \"remount\" : message.includes(\"Recovery was disabled\") || message.endsWith(\".\") ? \"warn-only\" : \"sync-dom\"; const kind = message.startsWith(\"Root mismatch\") ? \"root\" : message.startsWith(\"Element mismatch\") ? \"element\" : message.startsWith(\"Text mismatch\") || message.startsWith(\"Text content mismatch\") ? \"text\" : message.startsWith(\"Attribute mismatch\") ? \"attribute\" : message.startsWith(\"v-model\") ? \"model\" : message.startsWith(\"Extra DOM nodes\") ? \"extra-node\" : message.startsWith(\"Teleport\") ? \"teleport\" : message.startsWith(\"Component\") || message.startsWith(\"Dynamic component\") ? \"component\" : message.startsWith(\"Branch\") ? \"branch\" : message.startsWith(\"List\") ? \"list\" : \"hydration\"; const match = message.match(/expected\\s+(.+?),\\s+got\\s+(.+?)(?:[.;]|$)/); return { action, kind, expected: match?.[1], actual: match?.[2] }; };");
43
- emit(context, 1, "const __mikuru_hydrationDiagnostic = (message, details = {}) => { const inferred = __mikuru_inferDiagnostic(message); const node = details.node; const diagnostic = { ...__mikuru_componentInfo, phase: \"hydration\", message, ...inferred, ...details }; delete diagnostic.node; if (!diagnostic.domPath && node) diagnostic.domPath = __mikuru_domPath(node); return diagnostic; };");
44
- emit(context, 1, "const __mikuru_warn = (message, details = {}) => { const diagnostic = __mikuru_hydrationDiagnostic(message, details); __mikuru_emitDebug(\"hydration:warning\", diagnostic); if (typeof console !== \"undefined\" && console.warn) console.warn(`[Mikuru hydration] ${message} (phase: ${diagnostic.phase}, component: ${diagnostic.component}, file: ${diagnostic.filename})`); };");
53
+ emit(context, 1, "const __mikuru_hydrationDiagnostic = (message, details = {}) => { const inferred = __mikuru_inferDiagnostic(message); const node = details.node; const diagnostic = { ...__mikuru_componentInfo, source: \"hydration\", level: \"warning\", phase: \"hydration\", message, ...inferred, ...details }; delete diagnostic.node; if (!diagnostic.domPath && node) diagnostic.domPath = __mikuru_domPath(node); return diagnostic; };");
54
+ emit(context, 1, "const __mikuru_warn = (message, details = {}) => { const diagnostic = __mikuru_hydrationDiagnostic(message, details); __mikuru_emitDebug(\"hydration:warning\", { ...diagnostic, diagnostic }); if (typeof console !== \"undefined\" && console.warn) console.warn(`[Mikuru hydration] ${message} (phase: ${diagnostic.phase}, component: ${diagnostic.component}, file: ${diagnostic.filename})`); };");
45
55
  emit(context, 1, "const __mikuru_describeNode = (node) => { if (!node) return \"missing\"; if (node.nodeType === 1) return `<${node.tagName?.toLowerCase?.() ?? \"element\"}>`; if (node.nodeType === 3) return `text(${JSON.stringify(node.nodeValue ?? \"\")})`; if (node.nodeType === 8) return `comment(${JSON.stringify(node.nodeValue ?? \"\")})`; return `nodeType(${node.nodeType})`; };");
46
56
  emit(context, 1, "const __mikuru_restoreRegistrar = () => { if (__mikuru_previousRegistrar === undefined) { delete globalThis.__mikuru_currentRegistrar; } else { globalThis.__mikuru_currentRegistrar = __mikuru_previousRegistrar; } };");
47
57
  emit(context, 1, "const __mikuru_recovery = {};");
@@ -158,43 +168,52 @@ function hydrateChildren(context, rawChildren, parentVar, indent) {
158
168
  const children = rawChildren.filter(isHydratableNode);
159
169
  const domIndexVar = nextName(context, "domIndex");
160
170
  emit(context, indent, `let ${domIndexVar} = 0;`);
161
- children.forEach((child) => {
171
+ for (let index = 0; index < children.length; index += 1) {
172
+ const child = children[index];
162
173
  if (child.type === "element" && child.tag === "Teleport") {
163
174
  hydrateTeleportAtIndex(context, child, parentVar, domIndexVar, indent);
164
- return;
175
+ continue;
165
176
  }
166
177
  if (child.type === "element" && child.tag === "component") {
167
178
  hydrateDynamicComponentAtIndex(context, child, parentVar, domIndexVar, indent);
168
- return;
179
+ continue;
169
180
  }
170
181
  if (child.type === "element" && child.tag === "KeepAlive") {
171
182
  hydrateKeepAliveAtIndex(context, child, parentVar, domIndexVar, indent);
172
- return;
183
+ continue;
173
184
  }
174
185
  if (child.type === "element" && child.tag === "AsyncBoundary") {
175
186
  hydrateAsyncBoundaryAtIndex(context, child, parentVar, domIndexVar, indent);
176
- return;
187
+ continue;
177
188
  }
178
189
  if (child.type === "element" && child.tag === "ErrorBoundary") {
179
190
  hydrateErrorBoundaryAtIndex(context, child, parentVar, domIndexVar, indent);
180
- return;
191
+ continue;
181
192
  }
182
193
  if (child.type === "element" && child.tag === "TransitionGroup") {
183
194
  hydrateTransitionGroupAtIndex(context, child, parentVar, domIndexVar, indent);
184
- return;
195
+ continue;
185
196
  }
186
197
  if (child.type === "element" && child.tag === "Transition") {
187
198
  hydrateTransitionAtIndex(context, child, parentVar, domIndexVar, indent);
188
- return;
199
+ continue;
189
200
  }
190
201
  if (child.type === "element" && getAttr(child, "v-if") && !getAttr(child, "v-pre")) {
191
- hydrateIf(context, child, parentVar, domIndexVar, indent);
192
- emit(context, indent, `${domIndexVar} += 1;`);
193
- return;
202
+ const { branches, nextIndex } = collectIfBranches(children, index);
203
+ hydrateIfChain(context, branches, parentVar, domIndexVar, indent);
204
+ index = nextIndex - 1;
205
+ continue;
206
+ }
207
+ if (child.type === "element" && (getAttr(child, "v-else-if") || getAttr(child, "v-else"))) {
208
+ continue;
194
209
  }
195
210
  if (child.type === "element" && getAttr(child, "v-for") && !getAttr(child, "v-pre")) {
196
211
  hydrateFor(context, child, parentVar, domIndexVar, indent, domIndexVar);
197
- return;
212
+ continue;
213
+ }
214
+ if (child.type === "element" && child.tag === "template") {
215
+ hydrateFragmentChildrenAtIndex(context, child.children, parentVar, domIndexVar, indent);
216
+ continue;
198
217
  }
199
218
  const childVar = nextName(context, "node");
200
219
  emit(context, indent, `const ${childVar} = ${parentVar}.childNodes[${domIndexVar}];`);
@@ -212,7 +231,7 @@ function hydrateChildren(context, rawChildren, parentVar, indent) {
212
231
  emit(context, indent, "}");
213
232
  }
214
233
  emit(context, indent, `${domIndexVar} += 1;`);
215
- });
234
+ }
216
235
  emit(context, indent, `if (${parentVar}.childNodes.length > ${domIndexVar}) { __mikuru_warn("Extra DOM nodes after hydration: " + Array.from(${parentVar}.childNodes).slice(${domIndexVar}).map(__mikuru_describeNode).join(", ") + "."); }`);
217
236
  }
218
237
  function hydrateTeleportAtIndex(context, node, parentVar, domIndex, indent) {
@@ -271,6 +290,52 @@ function hydrateIf(context, node, parentVar, domIndex, indent) {
271
290
  emit(context, indent + 1, "__mikuru_recover(\"Branch mismatch: expected no v-if DOM for initial state\");");
272
291
  emit(context, indent, "}");
273
292
  }
293
+ function hydrateIfChain(context, branches, parentVar, domIndex, indent) {
294
+ branches.forEach((branch, index) => {
295
+ if (index === 0) {
296
+ emit(context, indent, `if (unwrap(${compileHydrationExpression(context, branch.condition ?? "", "v-if")})) {`);
297
+ }
298
+ else if (branch.directive === "v-else-if") {
299
+ emit(context, indent, `else if (unwrap(${compileHydrationExpression(context, branch.condition ?? "", "v-else-if")})) {`);
300
+ }
301
+ else {
302
+ emit(context, indent, "else {");
303
+ }
304
+ hydrateFragmentChildrenAtIndex(context, ifBranchChildren(branch.node), parentVar, domIndex, indent + 1);
305
+ emit(context, indent, "}");
306
+ });
307
+ }
308
+ function ifBranchChildren(node) {
309
+ if (node.tag === "template") {
310
+ return node.children;
311
+ }
312
+ return [withoutAttrs(node, ["v-if", "v-else-if", "v-else"])];
313
+ }
314
+ function collectIfBranches(children, startIndex) {
315
+ const first = children[startIndex];
316
+ if (first?.type !== "element") {
317
+ return { branches: [], nextIndex: startIndex + 1 };
318
+ }
319
+ const branches = [{ node: first, condition: getAttrValue(first, "v-if"), directive: "v-if" }];
320
+ let cursor = startIndex + 1;
321
+ while (cursor < children.length) {
322
+ const candidate = children[cursor];
323
+ if (candidate?.type !== "element") {
324
+ break;
325
+ }
326
+ if (getAttr(candidate, "v-else-if")) {
327
+ branches.push({ node: candidate, condition: getAttrValue(candidate, "v-else-if"), directive: "v-else-if" });
328
+ cursor += 1;
329
+ continue;
330
+ }
331
+ if (getAttr(candidate, "v-else")) {
332
+ branches.push({ node: candidate, directive: "v-else" });
333
+ cursor += 1;
334
+ }
335
+ break;
336
+ }
337
+ return { branches, nextIndex: cursor };
338
+ }
274
339
  function hydrateFor(context, node, parentVar, domIndex, indent, advanceVar) {
275
340
  const attr = getAttr(node, "v-for");
276
341
  if (!attr || attr.value === true) {
@@ -279,6 +344,22 @@ function hydrateFor(context, node, parentVar, domIndex, indent, advanceVar) {
279
344
  const forExpression = parseForExpression(String(attr.value));
280
345
  const listVar = nextName(context, "list");
281
346
  const itemVar = nextName(context, "item");
347
+ const startIndexVar = nextName(context, "forStartIndex");
348
+ emit(context, indent, `const ${startIndexVar} = ${domIndex};`);
349
+ if (node.tag === "template") {
350
+ emit(context, indent, `const ${listVar} = Array.from(unwrap(${compileHydrationExpression(context, forExpression.source, "v-for source")}) ?? []);`);
351
+ emit(context, indent, `for (const [__mikuru_index, ${itemVar}] of ${listVar}.entries()) {`);
352
+ emit(context, indent + 1, `const ${forExpression.item} = ${itemVar};`);
353
+ if (forExpression.index) {
354
+ emit(context, indent + 1, `const ${forExpression.index} = __mikuru_index;`);
355
+ }
356
+ withTemplateRefMode(context, "array", () => {
357
+ hydrateFragmentChildrenAtIndex(context, node.children, parentVar, domIndex, indent + 1);
358
+ });
359
+ emit(context, indent, "}");
360
+ emitHydratedForUpdate(context, node, parentVar, startIndexVar, domIndex, indent, forExpression);
361
+ return;
362
+ }
282
363
  const childVar = nextName(context, "row");
283
364
  const elementCheck = isComponentTag(node.tag)
284
365
  ? `!${childVar} || ${childVar}.nodeType !== 1`
@@ -299,6 +380,61 @@ function hydrateFor(context, node, parentVar, domIndex, indent, advanceVar) {
299
380
  if (advanceVar) {
300
381
  emit(context, indent, `${advanceVar} += ${listVar}.length;`);
301
382
  }
383
+ emitHydratedForUpdate(context, node, parentVar, startIndexVar, domIndex, indent, forExpression);
384
+ }
385
+ function emitHydratedForUpdate(context, node, parentVar, startIndexVar, endIndexVar, indent, forExpression) {
386
+ const hydratedFirstVar = nextName(context, "forHydratedFirst");
387
+ const hydratedAfterVar = nextName(context, "forHydratedAfter");
388
+ const startAnchorVar = nextName(context, "forUpdateStart");
389
+ const endAnchorVar = nextName(context, "forUpdateEnd");
390
+ const initializedVar = nextName(context, "forInitialized");
391
+ const branchCleanupVar = nextName(context, "forCleanup");
392
+ const stopVar = nextName(context, "stop");
393
+ const sourceVar = nextName(context, "forSource");
394
+ const indexVar = nextName(context, "forIndex");
395
+ const rawItemVar = nextName(context, "forItem");
396
+ emit(context, indent, `const ${hydratedFirstVar} = ${parentVar}.childNodes[${startIndexVar}] ?? null;`);
397
+ emit(context, indent, `const ${hydratedAfterVar} = ${parentVar}.childNodes[${endIndexVar}] ?? null;`);
398
+ emit(context, indent, `let ${startAnchorVar};`);
399
+ emit(context, indent, `let ${endAnchorVar};`);
400
+ emit(context, indent, `let ${initializedVar} = false;`);
401
+ emit(context, indent, `const ${branchCleanupVar} = [];`);
402
+ emit(context, indent, `const ${stopVar} = effect(() => {`);
403
+ emit(context, indent + 1, `const ${sourceVar} = Array.from(unwrap(${compileHydrationExpression(context, forExpression.source, "v-for source")}) ?? []);`);
404
+ emit(context, indent + 1, `if (!${initializedVar}) { ${initializedVar} = true; return; }`);
405
+ emit(context, indent + 1, `if (!${startAnchorVar}) {`);
406
+ emit(context, indent + 2, `${startAnchorVar} = document.createComment("for");`);
407
+ emit(context, indent + 2, `${endAnchorVar} = document.createComment("/for");`);
408
+ emit(context, indent + 2, `${parentVar}.insertBefore(${startAnchorVar}, ${hydratedFirstVar}?.parentNode === ${parentVar} ? ${hydratedFirstVar} : ${hydratedAfterVar});`);
409
+ emit(context, indent + 2, `${parentVar}.insertBefore(${endAnchorVar}, ${hydratedAfterVar});`);
410
+ emit(context, indent + 1, `}`);
411
+ emit(context, indent + 1, `__mikuru_runCleanup(${branchCleanupVar});`);
412
+ emitHydrationRemoveBetween(context, indent + 1, startAnchorVar, endAnchorVar);
413
+ emit(context, indent + 1, `for (let ${indexVar} = 0; ${indexVar} < ${sourceVar}.length; ${indexVar} += 1) {`);
414
+ emit(context, indent + 2, `const ${rawItemVar} = ${sourceVar}[${indexVar}];`);
415
+ emit(context, indent + 2, `const ${forExpression.item} = ${rawItemVar};`);
416
+ if (forExpression.index) {
417
+ emit(context, indent + 2, `const ${forExpression.index} = ${indexVar};`);
418
+ }
419
+ const previousTemplateRefMode = context.templateRefMode;
420
+ context.templateRefMode = "array";
421
+ try {
422
+ if (node.tag === "template") {
423
+ generateDomChildren(context, node.children, parentVar, branchCleanupVar, indent + 2, endAnchorVar);
424
+ }
425
+ else {
426
+ generateDomNode(context, withoutDomForAttrs(node), parentVar, branchCleanupVar, indent + 2, endAnchorVar);
427
+ }
428
+ }
429
+ finally {
430
+ context.templateRefMode = previousTemplateRefMode;
431
+ }
432
+ emit(context, indent + 1, `}`);
433
+ emit(context, indent, `});`);
434
+ emit(context, indent, `__mikuru_cleanup.push(() => {`);
435
+ emit(context, indent + 1, `${stopVar}();`);
436
+ emit(context, indent + 1, `__mikuru_runCleanup(${branchCleanupVar});`);
437
+ emit(context, indent, `});`);
302
438
  }
303
439
  function hydrateAttrs(context, node, elementVar, indent) {
304
440
  const staticClass = getStaticAttrValue(node, "class");
@@ -848,43 +984,52 @@ function hydrateTransitionGroupElement(context, node, elementVar, indent) {
848
984
  }
849
985
  function hydrateFragmentChildrenAtIndex(context, rawChildren, parentVar, domIndex, indent) {
850
986
  const children = rawChildren.filter(isHydratableNode);
851
- children.forEach((child) => {
987
+ for (let index = 0; index < children.length; index += 1) {
988
+ const child = children[index];
852
989
  if (child.type === "element" && child.tag === "Teleport") {
853
990
  hydrateTeleportAtIndex(context, child, parentVar, domIndex, indent);
854
- return;
991
+ continue;
855
992
  }
856
993
  if (child.type === "element" && child.tag === "component") {
857
994
  hydrateDynamicComponentAtIndex(context, child, parentVar, domIndex, indent);
858
- return;
995
+ continue;
859
996
  }
860
997
  if (child.type === "element" && child.tag === "KeepAlive") {
861
998
  hydrateKeepAliveAtIndex(context, child, parentVar, domIndex, indent);
862
- return;
999
+ continue;
863
1000
  }
864
1001
  if (child.type === "element" && child.tag === "AsyncBoundary") {
865
1002
  hydrateAsyncBoundaryAtIndex(context, child, parentVar, domIndex, indent);
866
- return;
1003
+ continue;
867
1004
  }
868
1005
  if (child.type === "element" && child.tag === "ErrorBoundary") {
869
1006
  hydrateErrorBoundaryAtIndex(context, child, parentVar, domIndex, indent);
870
- return;
1007
+ continue;
871
1008
  }
872
1009
  if (child.type === "element" && child.tag === "TransitionGroup") {
873
1010
  hydrateTransitionGroupAtIndex(context, child, parentVar, domIndex, indent);
874
- return;
1011
+ continue;
875
1012
  }
876
1013
  if (child.type === "element" && child.tag === "Transition") {
877
1014
  hydrateTransitionAtIndex(context, child, parentVar, domIndex, indent);
878
- return;
1015
+ continue;
879
1016
  }
880
1017
  if (child.type === "element" && getAttr(child, "v-if") && !getAttr(child, "v-pre")) {
881
- hydrateIf(context, child, parentVar, domIndex, indent);
882
- emit(context, indent, `${domIndex} += 1;`);
883
- return;
1018
+ const { branches, nextIndex } = collectIfBranches(children, index);
1019
+ hydrateIfChain(context, branches, parentVar, domIndex, indent);
1020
+ index = nextIndex - 1;
1021
+ continue;
1022
+ }
1023
+ if (child.type === "element" && (getAttr(child, "v-else-if") || getAttr(child, "v-else"))) {
1024
+ continue;
884
1025
  }
885
1026
  if (child.type === "element" && getAttr(child, "v-for") && !getAttr(child, "v-pre")) {
886
1027
  hydrateFor(context, child, parentVar, domIndex, indent, domIndex);
887
- return;
1028
+ continue;
1029
+ }
1030
+ if (child.type === "element" && child.tag === "template") {
1031
+ hydrateFragmentChildrenAtIndex(context, child.children, parentVar, domIndex, indent);
1032
+ continue;
888
1033
  }
889
1034
  const childVar = nextName(context, "node");
890
1035
  emit(context, indent, `const ${childVar} = ${parentVar}.childNodes[${domIndex}];`);
@@ -902,7 +1047,7 @@ function hydrateFragmentChildrenAtIndex(context, rawChildren, parentVar, domInde
902
1047
  emit(context, indent, "}");
903
1048
  }
904
1049
  emit(context, indent, `${domIndex} += 1;`);
905
- });
1050
+ }
906
1051
  }
907
1052
  function hydrateComponentProps(context, node, propsVar, indent) {
908
1053
  for (const attr of node.attrs) {
@@ -972,6 +1117,16 @@ function hydrateComponentProps(context, node, propsVar, indent) {
972
1117
  emit(context, indent, `${propsVar}[${quote(attr.name)}] = ${attr.value === true ? "true" : quote(attr.value)};`);
973
1118
  }
974
1119
  }
1120
+ function emitHydrationRemoveBetween(context, indent, startVar, endVar) {
1121
+ const currentVar = nextName(context, "current");
1122
+ const nextVarName = nextName(context, "next");
1123
+ emit(context, indent, `let ${currentVar} = ${startVar}.nextSibling;`);
1124
+ emit(context, indent, `while (${currentVar} && ${currentVar} !== ${endVar}) {`);
1125
+ emit(context, indent + 1, `const ${nextVarName} = ${currentVar}.nextSibling;`);
1126
+ emit(context, indent + 1, `__mikuru_removeNode(${currentVar});`);
1127
+ emit(context, indent + 1, `${currentVar} = ${nextVarName};`);
1128
+ emit(context, indent, `}`);
1129
+ }
975
1130
  function hydrateEvents(context, node, elementVar, indent) {
976
1131
  for (const attr of node.attrs) {
977
1132
  const objectEvent = parseObjectOnDirective(attr.name);