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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.24 - 2026-05-14
4
+
5
+ - Added Mikuru-native `m-*` directive syntax across DOM rendering, SSR, and hydration while keeping `v-*` as compatibility aliases.
6
+ - Added debug compiler warnings for `v-*` compatibility aliases, pointing users to the matching `m-*` directive spelling.
7
+ - Updated README, docs, examples, and templates to present `m-*` as the recommended directive syntax.
8
+ - Added shared Debug Inspector diagnostic payloads for compiler, runtime, router, SSR, and hydration warning/error events.
9
+ - Added support for `<template m-if>` / `<template m-else-if>` / `<template m-else>` fragment branches across DOM rendering, SSR, and hydration.
10
+ - Added support for keyed `<template m-for>` fragment rows across DOM rendering, SSR, and hydration.
11
+ - Expanded `<template m-for>` coverage for unkeyed fragments, nested fragment loops, and nested `m-if` / `m-else` branches.
12
+ - Added hydrated `m-for` reconciliation so element and `<template m-for>` lists can append, remove, replace, and recreate rows after SSR hydration.
13
+
3
14
  ## 1.0.23 - 2026-05-13
4
15
 
5
16
  - Added structured hydration diagnostic payloads with warning kind, recovery action, inferred expected/actual values, and DOM path context.
package/README.md CHANGED
@@ -132,23 +132,24 @@ declare const Greeting: MikuruComponent<GreetingProps>;
132
132
  - `.mikuru` SFCs with `<template>`, `<script>`, and `<style>`
133
133
  - Vite plugin support through `mikuru/vite`
134
134
  - Template interpolation with `{{ value }}`
135
- - DOM events with `@click`, `v-on:click`, inline handlers, object-form option modifiers, `.prevent`, `.stop`, `.self`, `.once`, `.capture`, `.passive`, key, mouse button, system, and `.exact` modifiers
135
+ - DOM events with `@click`, `m-on:click`, inline handlers, object-form option modifiers, `.prevent`, `.stop`, `.self`, `.once`, `.capture`, `.passive`, key, mouse button, system, and `.exact` modifiers
136
136
  - Component events with `@select` and `.once`
137
- - Attribute bindings with normalized `:class` and `:style`, boolean/form property sync, direct/object `v-bind` modifiers like `.prop`, `.attr`, and `.camel`, plus dynamic arguments like `:[name]` and `@[event]`
138
- - `v-if`, `v-else-if`, `v-else`, `v-show`, `v-for`, `v-html`, `v-text`, `v-pre`, and `v-cloak`
139
- - `v-model` for common form controls, checkbox arrays, radio groups, multiple selects, modifiers, and named child component models
137
+ - Attribute bindings with normalized `:class` and `:style`, boolean/form property sync, direct/object `m-bind` modifiers like `.prop`, `.attr`, and `.camel`, plus dynamic arguments like `:[name]` and `@[event]`
138
+ - `m-if`, `m-else-if`, `m-else`, `m-show`, `m-for`, `m-html`, `m-text`, `m-pre`, and `m-cloak`
139
+ - `m-model` for common form controls, checkbox arrays, radio groups, multiple selects, modifiers, and named child component models
140
+ - `v-*` directive spellings remain available as compatibility aliases for existing components and Vue-oriented migrations
140
141
  - Component props, events, DOM attribute fallthrough, `useAttrs`, template refs, `defineProps`, `defineEmits`, default slots, named/dynamic slots, and slot props with simple defaults
141
- - CSS class transitions with built-in `<Transition name="fade">`, `v-if` chains, dynamic components, class overrides, `appear`, `mode="out-in"`, and `<TransitionGroup>` for keyed lists
142
+ - CSS class transitions with built-in `<Transition name="fade">`, `m-if` chains, dynamic components, class overrides, `appear`, `mode="out-in"`, and `<TransitionGroup>` for keyed lists
142
143
  - Built-in `<Teleport to="#target">` for rendering content outside the current DOM position
143
144
  - Built-in `<AsyncBoundary :loading :fallback :delay :timeout>` for grouped async loading, delayed loading UI, boundary timeouts, and retryable async failures with aggregated fallback errors
144
145
  - Built-in `<ErrorBoundary :fallback>` for local component mount, descendant event handler, lifecycle, and cleanup fallbacks, with `errorInfo`, `retry`, `reset`, and `:reset-key` recovery
145
146
  - Runtime helpers including `ref`, `isRef`, `unref`, `toRef`, `toRefs`, `reactive`, `readonly`, lazy cached read-only and writable `computed`, `effect` with optional scheduling, `queueJob`/`flushJobs`, `watch`, `watchEffect` with cleanup callbacks, `nextTick`, lifecycle callbacks including KeepAlive activation hooks, `provide`, `inject`, and `defineAsyncComponent` with ErrorBoundary handoff and SSR loader resolution
146
147
  - Routing through `mikuru/router` with route matching, history/hash/memory histories, guards, router context helpers, and `RouterView` / `RouterLink` across mount, SSR, and hydration
147
- - SSR through `compileSsr()` and `mikuru/server`, covering escaped text, static and bound attributes, content directives, `v-pre`, `v-cloak`, `v-if` chains, `v-for`, async child components, props, named/default slots, scoped slot props, component tree context, Teleport collection, string and async iterable stream rendering, and router route rendering with context propagation
148
- - Hydration through `compileHydration()` and `hydrateRoute()`, reusing existing SSR DOM while attaching events, syncing text/attributes, recovering structural mismatches with an opt-out remount fallback, hydrating component context/lifecycle hooks, `v-show`, DOM and component `v-model`, `v-pre`, `v-cloak`, initial `v-if` / `v-for` DOM, Teleport target and disabled inline content, delegating child and route components to `hydrate()` when available, and optionally starting router history listening after route hydration
148
+ - SSR through `compileSsr()` and `mikuru/server`, covering escaped text, static and bound attributes, content directives, `m-pre`, `m-cloak`, `m-if` chains, `m-for`, async child components, props, named/default slots, scoped slot props, component tree context, Teleport collection, string and async iterable stream rendering, and router route rendering with context propagation
149
+ - Hydration through `compileHydration()` and `hydrateRoute()`, reusing existing SSR DOM while attaching events, syncing text/attributes, recovering structural mismatches with an opt-out remount fallback, hydrating component context/lifecycle hooks, `m-show`, DOM and component `m-model`, `m-pre`, `m-cloak`, initial `m-if` / `m-for` DOM, Teleport target and disabled inline content, delegating child and route components to `hydrate()` when available, and optionally starting router history listening after route hydration
149
150
  - Style injection and basic `<style scoped>` selector rewriting
150
151
  - Compile errors with filenames, line/column information, code frames, and typo suggestions for built-in attributes, directives, and modifiers
151
- - Debug diagnostics with optional generated `sourceURL`, unstable devtools metadata/events, and hydration warnings that include phase, component, and filename context
152
+ - Debug diagnostics with optional generated `sourceURL`, `v-*` compatibility warnings, unstable devtools metadata/events, and hydration warnings that include phase, component, and filename context
152
153
 
153
154
  ## Package Exports
154
155
 
@@ -199,8 +200,8 @@ npx mikuru --list-templates
199
200
  <template>
200
201
  <button @click="open = !open">Toggle</button>
201
202
  <Transition name="fade">
202
- <p v-if="open">Saved changes</p>
203
- <p v-else>Waiting for edits</p>
203
+ <p m-if="open">Saved changes</p>
204
+ <p m-else>Waiting for edits</p>
204
205
  </Transition>
205
206
  </template>
206
207
 
@@ -0,0 +1,8 @@
1
+ import type { ElementNode } from "./types.js";
2
+ type CompatDirectiveDiagnosticOptions = {
3
+ debug?: boolean;
4
+ filename?: string;
5
+ phase: string;
6
+ };
7
+ export declare function emitCompatDirectiveDiagnostics(ast: ElementNode, options: CompatDirectiveDiagnosticOptions): void;
8
+ export {};
@@ -0,0 +1,45 @@
1
+ import { emitDebugDiagnostic } from "../runtime/devtools.js";
2
+ export function emitCompatDirectiveDiagnostics(ast, options) {
3
+ if (options.debug !== true) {
4
+ return;
5
+ }
6
+ walkElement(ast, (node) => {
7
+ for (const attr of node.attrs) {
8
+ if (!isLegacyDirectiveAttr(attr)) {
9
+ continue;
10
+ }
11
+ const sourceName = attr.sourceName ?? attr.name;
12
+ const preferredName = preferredDirectiveName(sourceName);
13
+ emitDebugDiagnostic("compiler", "warning", `${sourceName} is supported as a compatibility alias. Prefer ${preferredName} in Mikuru components.`, {
14
+ phase: options.phase,
15
+ filename: options.filename,
16
+ directive: sourceName,
17
+ preferredDirective: preferredName,
18
+ tag: node.tag,
19
+ loc: attr.loc
20
+ });
21
+ }
22
+ });
23
+ }
24
+ function walkElement(node, visit) {
25
+ visit(node);
26
+ if (node.attrs.some((attr) => attr.name === "v-pre")) {
27
+ return;
28
+ }
29
+ for (const child of node.children) {
30
+ if (isElementNode(child)) {
31
+ walkElement(child, visit);
32
+ }
33
+ }
34
+ }
35
+ function isElementNode(node) {
36
+ return node.type === "element";
37
+ }
38
+ function isLegacyDirectiveAttr(attr) {
39
+ const sourceName = attr.sourceName ?? attr.name;
40
+ return sourceName.startsWith("v-");
41
+ }
42
+ function preferredDirectiveName(name) {
43
+ return name.startsWith("v-") ? `m-${name.slice("v-".length)}` : name;
44
+ }
45
+ //# sourceMappingURL=compatDiagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compatDiagnostics.js","sourceRoot":"","sources":["../../src/compiler/compatDiagnostics.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAQ7D,MAAM,UAAU,8BAA8B,CAAC,GAAgB,EAAE,OAAyC;IACxG,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO;IACT,CAAC;IAED,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE;QACxB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjC,SAAS;YACX,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC;YAChD,MAAM,aAAa,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC;YACzD,mBAAmB,CACjB,UAAU,EACV,SAAS,EACT,GAAG,UAAU,kDAAkD,aAAa,wBAAwB,EACpG;gBACE,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,SAAS,EAAE,UAAU;gBACrB,kBAAkB,EAAE,aAAa;gBACjC,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,GAAG,EAAE,IAAI,CAAC,GAAG;aACd,CACF,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,IAAiB,EAAE,KAAkC;IACxE,KAAK,CAAC,IAAI,CAAC,CAAC;IAEZ,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,CAAC;QACrD,OAAO;IACT,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAkB;IACvC,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC;AACjC,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAuB;IACpD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC;IAChD,OAAO,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACvE,CAAC"}
@@ -1,24 +1,37 @@
1
1
  import { analyzeTemplate } from "./analyzeTemplate.js";
2
+ import { emitCompatDirectiveDiagnostics } from "./compatDiagnostics.js";
2
3
  import { generate } from "./generate.js";
3
4
  import { parseSfc } from "./parseSfc.js";
4
5
  import { parseTemplate } from "./parseTemplate.js";
5
6
  import { createSourceMap } from "./sourceMap.js";
7
+ import { emitDebugDiagnostic } from "../runtime/devtools.js";
6
8
  export function compile(source, options = {}) {
7
- const descriptor = parseSfc(source, options.filename);
8
- const ast = parseTemplate(descriptor.template, {
9
- filename: options.filename,
10
- source,
11
- offset: descriptor.templateOffset
12
- });
13
- const bindings = analyzeTemplate(ast, { source, filename: options.filename });
14
- const code = generate(descriptor, ast, { debug: options.debug === true, batchedUpdates: options.batchedUpdates === true });
15
- const map = createSourceMap(code, descriptor, ast);
16
- return {
17
- code,
18
- map,
19
- descriptor,
20
- ast,
21
- bindings
22
- };
9
+ try {
10
+ const descriptor = parseSfc(source, options.filename);
11
+ const ast = parseTemplate(descriptor.template, {
12
+ filename: options.filename,
13
+ source,
14
+ offset: descriptor.templateOffset
15
+ });
16
+ emitCompatDirectiveDiagnostics(ast, { debug: options.debug === true, filename: options.filename, phase: "compile" });
17
+ const bindings = analyzeTemplate(ast, { source, filename: options.filename });
18
+ const code = generate(descriptor, ast, { debug: options.debug === true, batchedUpdates: options.batchedUpdates === true });
19
+ const map = createSourceMap(code, descriptor, ast);
20
+ return {
21
+ code,
22
+ map,
23
+ descriptor,
24
+ ast,
25
+ bindings
26
+ };
27
+ }
28
+ catch (error) {
29
+ emitDebugDiagnostic("compiler", "error", error instanceof Error ? error.message : String(error), {
30
+ phase: "compile",
31
+ filename: options.filename,
32
+ error
33
+ });
34
+ throw error;
35
+ }
23
36
  }
24
37
  //# sourceMappingURL=compile.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/compiler/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,UAA0B,EAAE;IAClE,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE;QAC7C,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,MAAM;QACN,MAAM,EAAE,UAAU,CAAC,cAAc;KAClC,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC,CAAC;IAC3H,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;IAEnD,OAAO;QACL,IAAI;QACJ,GAAG;QACH,UAAU;QACV,GAAG;QACH,QAAQ;KACT,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/compiler/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,UAA0B,EAAE;IAClE,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE;YAC7C,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM;YACN,MAAM,EAAE,UAAU,CAAC,cAAc;SAClC,CAAC,CAAC;QACH,8BAA8B,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QACrH,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9E,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC,CAAC;QAC3H,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QAEnD,OAAO;YACL,IAAI;YACJ,GAAG;YACH,UAAU;YACV,GAAG;YACH,QAAQ;SACT,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mBAAmB,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YAC/F,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK;SACN,CAAC,CAAC;QACH,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -1,27 +1,40 @@
1
1
  import { analyzeTemplate } from "./analyzeTemplate.js";
2
+ import { emitCompatDirectiveDiagnostics } from "./compatDiagnostics.js";
2
3
  import { generate } from "./generate.js";
3
4
  import { generateHydration } from "./generateHydration.js";
4
5
  import { parseSfc } from "./parseSfc.js";
5
6
  import { parseTemplate } from "./parseTemplate.js";
6
7
  import { createSourceMap } from "./sourceMap.js";
8
+ import { emitDebugDiagnostic } from "../runtime/devtools.js";
7
9
  export function compileHydration(source, options = {}) {
8
- const descriptor = parseSfc(source, options.filename);
9
- const ast = parseTemplate(descriptor.template, {
10
- filename: options.filename,
11
- source,
12
- offset: descriptor.templateOffset
13
- });
14
- const bindings = analyzeTemplate(ast, { source, filename: options.filename });
15
- const mountCode = generate(descriptor, ast, { debug: options.debug === true, batchedUpdates: options.batchedUpdates === true });
16
- const hydrationCode = generateHydration(descriptor, ast, { includeImports: false });
17
- const code = mountCode.replace("export default __mikuru_component;", `${hydrationCode}\nconst __mikuru_hydrationComponent = { ...__mikuru_component, hydrate };\nexport default __mikuru_hydrationComponent;`);
18
- const map = createSourceMap(code, descriptor, ast);
19
- return {
20
- code,
21
- map,
22
- descriptor,
23
- ast,
24
- bindings
25
- };
10
+ try {
11
+ const descriptor = parseSfc(source, options.filename);
12
+ const ast = parseTemplate(descriptor.template, {
13
+ filename: options.filename,
14
+ source,
15
+ offset: descriptor.templateOffset
16
+ });
17
+ emitCompatDirectiveDiagnostics(ast, { debug: options.debug === true, filename: options.filename, phase: "compile-hydration" });
18
+ const bindings = analyzeTemplate(ast, { source, filename: options.filename });
19
+ const mountCode = generate(descriptor, ast, { debug: options.debug === true, batchedUpdates: options.batchedUpdates === true });
20
+ const hydrationCode = generateHydration(descriptor, ast, { includeImports: false });
21
+ const code = mountCode.replace("export default __mikuru_component;", `${hydrationCode}\nconst __mikuru_hydrationComponent = { ...__mikuru_component, hydrate };\nexport default __mikuru_hydrationComponent;`);
22
+ const map = createSourceMap(code, descriptor, ast);
23
+ return {
24
+ code,
25
+ map,
26
+ descriptor,
27
+ ast,
28
+ bindings
29
+ };
30
+ }
31
+ catch (error) {
32
+ emitDebugDiagnostic("compiler", "error", error instanceof Error ? error.message : String(error), {
33
+ phase: "compile-hydration",
34
+ filename: options.filename,
35
+ error
36
+ });
37
+ throw error;
38
+ }
26
39
  }
27
40
  //# sourceMappingURL=compileHydration.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compileHydration.js","sourceRoot":"","sources":["../../src/compiler/compileHydration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,UAA0B,EAAE;IAC3E,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE;QAC7C,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,MAAM;QACN,MAAM,EAAE,UAAU,CAAC,cAAc;KAClC,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9E,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC,CAAC;IAChI,MAAM,aAAa,GAAG,iBAAiB,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,oCAAoC,EAAE,GAAG,aAAa,wHAAwH,CAAC,CAAC;IAC/M,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;IAEnD,OAAO;QACL,IAAI;QACJ,GAAG;QACH,UAAU;QACV,GAAG;QACH,QAAQ;KACT,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"compileHydration.js","sourceRoot":"","sources":["../../src/compiler/compileHydration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,UAA0B,EAAE;IAC3E,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE;YAC7C,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM;YACN,MAAM,EAAE,UAAU,CAAC,cAAc;SAClC,CAAC,CAAC;QACH,8BAA8B,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;QAC/H,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9E,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC,CAAC;QAChI,MAAM,aAAa,GAAG,iBAAiB,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;QACpF,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,oCAAoC,EAAE,GAAG,aAAa,wHAAwH,CAAC,CAAC;QAC/M,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QAEnD,OAAO;YACL,IAAI;YACJ,GAAG;YACH,UAAU;YACV,GAAG;YACH,QAAQ;SACT,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mBAAmB,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YAC/F,KAAK,EAAE,mBAAmB;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK;SACN,CAAC,CAAC;QACH,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -1,21 +1,34 @@
1
1
  import { analyzeTemplate } from "./analyzeTemplate.js";
2
+ import { emitCompatDirectiveDiagnostics } from "./compatDiagnostics.js";
2
3
  import { generateSsr } from "./generateSsr.js";
3
4
  import { parseSfc } from "./parseSfc.js";
4
5
  import { parseTemplate } from "./parseTemplate.js";
6
+ import { emitDebugDiagnostic } from "../runtime/devtools.js";
5
7
  export function compileSsr(source, options = {}) {
6
- const descriptor = parseSfc(source, options.filename);
7
- const ast = parseTemplate(descriptor.template, {
8
- filename: options.filename,
9
- source,
10
- offset: descriptor.templateOffset
11
- });
12
- const bindings = analyzeTemplate(ast, { source, filename: options.filename });
13
- const code = generateSsr(descriptor, ast);
14
- return {
15
- code,
16
- descriptor,
17
- ast,
18
- bindings
19
- };
8
+ try {
9
+ const descriptor = parseSfc(source, options.filename);
10
+ const ast = parseTemplate(descriptor.template, {
11
+ filename: options.filename,
12
+ source,
13
+ offset: descriptor.templateOffset
14
+ });
15
+ emitCompatDirectiveDiagnostics(ast, { debug: options.debug === true, filename: options.filename, phase: "compile-ssr" });
16
+ const bindings = analyzeTemplate(ast, { source, filename: options.filename });
17
+ const code = generateSsr(descriptor, ast);
18
+ return {
19
+ code,
20
+ descriptor,
21
+ ast,
22
+ bindings
23
+ };
24
+ }
25
+ catch (error) {
26
+ emitDebugDiagnostic("compiler", "error", error instanceof Error ? error.message : String(error), {
27
+ phase: "compile-ssr",
28
+ filename: options.filename,
29
+ error
30
+ });
31
+ throw error;
32
+ }
20
33
  }
21
34
  //# sourceMappingURL=compileSsr.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compileSsr.js","sourceRoot":"","sources":["../../src/compiler/compileSsr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,UAA0B,EAAE;IACrE,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE;QAC7C,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,MAAM;QACN,MAAM,EAAE,UAAU,CAAC,cAAc;KAClC,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAE1C,OAAO;QACL,IAAI;QACJ,UAAU;QACV,GAAG;QACH,QAAQ;KACT,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"compileSsr.js","sourceRoot":"","sources":["../../src/compiler/compileSsr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,UAA0B,EAAE;IACrE,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE;YAC7C,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM;YACN,MAAM,EAAE,UAAU,CAAC,cAAc;SAClC,CAAC,CAAC;QACH,8BAA8B,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;QACzH,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9E,MAAM,IAAI,GAAG,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAE1C,OAAO;YACL,IAAI;YACJ,UAAU;YACV,GAAG;YACH,QAAQ;SACT,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mBAAmB,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YAC/F,KAAK,EAAE,aAAa;YACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK;SACN,CAAC,CAAC;QACH,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -1,7 +1,23 @@
1
- import type { ElementNode, SfcDescriptor } from "./types.js";
1
+ import type { ElementNode, SfcDescriptor, TemplateNode } from "./types.js";
2
+ export type GenerateContext = {
3
+ lines: string[];
4
+ index: number;
5
+ source?: string;
6
+ filename?: string;
7
+ scopeAttr?: string;
8
+ templateRefMode?: "single" | "array";
9
+ componentContextVar?: string;
10
+ debug?: boolean;
11
+ batchedUpdates?: boolean;
12
+ once?: boolean;
13
+ };
2
14
  type GenerateOptions = {
3
15
  debug?: boolean;
4
16
  batchedUpdates?: boolean;
5
17
  };
6
18
  export declare function generate(descriptor: SfcDescriptor, root: ElementNode, options?: GenerateOptions): string;
19
+ export declare function generateNode(context: GenerateContext, node: TemplateNode, parentVar: string, cleanupVar: string, indent: number, beforeVar?: string): string;
20
+ export declare function generateChildren(context: GenerateContext, children: TemplateNode[], parentVar: string, cleanupVar: string, indent: number, beforeVar?: string): void;
21
+ export declare function withoutForAttrs(node: ElementNode): ElementNode;
22
+ export declare function createScopeAttr(descriptor: SfcDescriptor): string;
7
23
  export {};
@@ -20,7 +20,7 @@ export function generate(descriptor, root, options = {}) {
20
20
  runtimeBaseImports.push("queueJob");
21
21
  }
22
22
  if (context.debug) {
23
- runtimeBaseImports.push("emitDebugEvent", "registerDebugComponent");
23
+ runtimeBaseImports.push("createDebugDiagnostic", "emitDebugEvent", "registerDebugComponent");
24
24
  }
25
25
  const runtimeImports = mergeRuntimeImports(runtimeBaseImports, script.runtimeImports);
26
26
  emit(context, 0, `import { ${runtimeImports.join(", ")} } from "mikuru/runtime";`);
@@ -41,7 +41,7 @@ export function generate(descriptor, root, options = {}) {
41
41
  emit(context, 1, "const __mikuru_errorInfo = (phase) => ({ ...__mikuru_componentInfo, phase });");
42
42
  emit(context, 1, "const __mikuru_reportError = (error, errorHandler = __mikuru_context.errorHandler, phase = \"runtime\") => {");
43
43
  if (context.debug) {
44
- emit(context, 2, "emitDebugEvent(\"component:error\", { component: __mikuru_componentInfo, error, errorInfo: __mikuru_errorInfo(phase), componentId: __mikuru_debug.id });");
44
+ emit(context, 2, "emitDebugEvent(\"component:error\", { component: __mikuru_componentInfo, error, errorInfo: __mikuru_errorInfo(phase), componentId: __mikuru_debug.id, diagnostic: createDebugDiagnostic(\"runtime\", \"error\", error instanceof Error ? error.message : String(error), { ...__mikuru_errorInfo(phase), error }) });");
45
45
  }
46
46
  emit(context, 2, "if (typeof errorHandler === \"function\") { Promise.resolve().then(() => errorHandler(error, __mikuru_errorInfo(phase))); return; }");
47
47
  emit(context, 2, "setTimeout(() => { throw error; });");
@@ -235,7 +235,7 @@ function emitDevtoolsRootUpdate(context, rootVar, indent) {
235
235
  emit(context, indent, `__mikuru_debug.update({ root: ${rootVar} });`);
236
236
  emit(context, indent, "emitDebugEvent(\"component:mount\", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id, root: __mikuru_debug.metadata.root, props: __mikuru_debug.metadata.props, attrs: __mikuru_debug.metadata.attrs });");
237
237
  }
238
- function generateNode(context, node, parentVar, cleanupVar, indent, beforeVar) {
238
+ export function generateNode(context, node, parentVar, cleanupVar, indent, beforeVar) {
239
239
  if (node.type === "text") {
240
240
  return generateText(context, node, parentVar, cleanupVar, indent, beforeVar);
241
241
  }
@@ -286,8 +286,28 @@ function generateNode(context, node, parentVar, cleanupVar, indent, beforeVar) {
286
286
  if (node.tag === "slot") {
287
287
  return generateSlot(context, node, parentVar, cleanupVar, indent, beforeVar);
288
288
  }
289
+ if (node.tag === "template") {
290
+ return generateTemplateFragment(context, node, parentVar, cleanupVar, indent, beforeVar);
291
+ }
289
292
  return generateElement(context, node, parentVar, cleanupVar, indent, beforeVar);
290
293
  }
294
+ function generateTemplateFragment(context, node, parentVar, cleanupVar, indent, beforeVar) {
295
+ validatePlainTemplate(context, node);
296
+ const startVar = nextVar(context, "templateStart");
297
+ const endVar = nextVar(context, "templateEnd");
298
+ emit(context, indent, `const ${startVar} = document.createComment("template");`);
299
+ emit(context, indent, `const ${endVar} = document.createComment("/template");`);
300
+ appendNode(context, parentVar, startVar, indent, beforeVar);
301
+ appendNode(context, parentVar, endVar, indent, beforeVar);
302
+ generateChildren(context, node.children, parentVar, cleanupVar, indent, endVar);
303
+ return startVar;
304
+ }
305
+ function validatePlainTemplate(context, node) {
306
+ const slotAttr = node.attrs.find((attr) => isSlotDirectiveAttr(attr));
307
+ if (slotAttr) {
308
+ throwTemplateError("Slot templates must be direct children of a component", context, slotAttr.loc);
309
+ }
310
+ }
291
311
  function generateTransition(context, node, parentVar, cleanupVar, indent, beforeVar) {
292
312
  validateTransitionAttributes(context, node);
293
313
  const children = getTransitionChildren(context, node);
@@ -420,7 +440,7 @@ function generateErrorBoundary(context, node, parentVar, cleanupVar, indent, bef
420
440
  emit(context, indent + 1, `if (!${fallbackVar} || typeof ${fallbackVar}.mount !== "function") { throw ${errorVar}; }`);
421
441
  emit(context, indent + 1, `const ${normalizedErrorInfoVar} = ${errorInfoVar} && typeof ${errorInfoVar} === "object" ? ${errorInfoVar} : {};`);
422
442
  if (context.debug) {
423
- emit(context, indent + 1, `emitDebugEvent("component:error", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id, error: ${errorVar}, errorInfo: { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo } });`);
443
+ emit(context, indent + 1, `emitDebugEvent("component:error", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id, error: ${errorVar}, errorInfo: { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo }, diagnostic: createDebugDiagnostic("runtime", "error", ${errorVar} instanceof Error ? ${errorVar}.message : String(${errorVar}), { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo, error: ${errorVar} }) });`);
424
444
  }
425
445
  emit(context, indent + 1, `const ${fallbackFragmentVar} = document.createDocumentFragment();`);
426
446
  emit(context, indent + 1, `const ${fallbackInstanceVar} = ${fallbackVar}.mount(${fallbackFragmentVar}, { error: ${errorVar}, errorInfo: { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo }, retry: ${renderVar}, reset: ${renderVar}, __mikuru_context });`);
@@ -546,7 +566,7 @@ function generateAsyncBoundary(context, node, parentVar, cleanupVar, indent, bef
546
566
  emit(context, indent + 1, `if (!${fallbackVar} || typeof ${fallbackVar}.mount !== "function") { throw ${errorVar}; }`);
547
567
  emit(context, indent + 1, `const ${normalizedErrorInfoVar} = ${errorInfoVar} && typeof ${errorInfoVar} === "object" ? ${errorInfoVar} : {};`);
548
568
  if (context.debug) {
549
- emit(context, indent + 1, `emitDebugEvent("async:rejected", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id, error: ${errorVar}, errors: [...${errorsVar}], pending: ${pendingVar}, errorInfo: { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo } });`);
569
+ emit(context, indent + 1, `emitDebugEvent("async:rejected", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id, error: ${errorVar}, errors: [...${errorsVar}], pending: ${pendingVar}, errorInfo: { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo }, diagnostic: createDebugDiagnostic("runtime", "error", ${errorVar} instanceof Error ? ${errorVar}.message : String(${errorVar}), { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo, error: ${errorVar} }) });`);
550
570
  }
551
571
  emit(context, indent + 1, `const ${fallbackFragmentVar} = document.createDocumentFragment();`);
552
572
  emit(context, indent + 1, `const ${fallbackInstanceVar} = ${fallbackVar}.mount(${fallbackFragmentVar}, { error: ${errorVar}, errors: [...${errorsVar}], errorInfo: { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo }, pending: ${pendingVar}, retry: ${lastRetryVar}, reset: ${lastRetryVar}, __mikuru_context });`);
@@ -1545,7 +1565,7 @@ function emitObjectListeners(context, elementVar, attr, cleanupVar, indent) {
1545
1565
  emit(context, indent + 1, `${listenersVar}.clear();`);
1546
1566
  emit(context, indent, "});");
1547
1567
  }
1548
- function generateChildren(context, children, parentVar, cleanupVar, indent, beforeVar) {
1568
+ export function generateChildren(context, children, parentVar, cleanupVar, indent, beforeVar) {
1549
1569
  let index = 0;
1550
1570
  while (index < children.length) {
1551
1571
  const child = children[index];
@@ -1656,6 +1676,9 @@ function generateFor(context, node, parentVar, cleanupVar, indent, expression, b
1656
1676
  const { item: itemName, index: indexName, source: sourceExpression } = parseForExpression(expression, toExpressionContext(context, getStringAttrLocation(node, "v-for")));
1657
1677
  const keyExpression = getKeyExpression(node);
1658
1678
  if (keyExpression) {
1679
+ if (node.tag === "template") {
1680
+ return generateKeyedTemplateFor(context, node, parentVar, cleanupVar, indent, itemName, indexName, sourceExpression, keyExpression, beforeVar);
1681
+ }
1659
1682
  return generateKeyedFor(context, node, parentVar, cleanupVar, indent, itemName, indexName, sourceExpression, keyExpression, beforeVar);
1660
1683
  }
1661
1684
  const startVar = nextVar(context, "forStart");
@@ -1696,6 +1719,110 @@ function generateFor(context, node, parentVar, cleanupVar, indent, expression, b
1696
1719
  emit(context, indent, "});");
1697
1720
  return startVar;
1698
1721
  }
1722
+ function generateKeyedTemplateFor(context, node, parentVar, cleanupVar, indent, itemName, indexName, sourceExpression, keyExpression, beforeVar) {
1723
+ validatePlainTemplate(context, node);
1724
+ const startVar = nextVar(context, "forStart");
1725
+ const endVar = nextVar(context, "forEnd");
1726
+ const recordsVar = nextVar(context, "forRecords");
1727
+ const stopVar = nextVar(context, "stop");
1728
+ const compiledSource = compileTemplateExpression(sourceExpression, "v-for source", toExpressionContext(context, getStringAttrLocation(node, "v-for")));
1729
+ const compiledKey = compileTemplateExpression(keyExpression, "v-for key", toExpressionContext(context, getKeyAttrLocation(node)));
1730
+ const compiledMemo = getMemoExpression(context, node) ?? getOnceMemoExpression(context, node);
1731
+ emit(context, indent, `const ${recordsVar} = new Map();`);
1732
+ emit(context, indent, `const ${startVar} = document.createComment("for");`);
1733
+ emit(context, indent, `const ${endVar} = document.createComment("/for");`);
1734
+ appendNode(context, parentVar, startVar, indent, beforeVar);
1735
+ appendNode(context, parentVar, endVar, indent, beforeVar);
1736
+ emit(context, indent, `const ${stopVar} = effect(() => {`);
1737
+ const sourceVar = nextVar(context, "forSource");
1738
+ const nextRecordsVar = nextVar(context, "nextRecords");
1739
+ const indexVar = nextVar(context, "forIndex");
1740
+ const rawItemVar = nextVar(context, "forItem");
1741
+ const rawIndexVar = nextVar(context, "forIndexValue");
1742
+ const keyVar = nextVar(context, "forKey");
1743
+ const recordVar = nextVar(context, "forRecord");
1744
+ const recordCleanupVar = nextVar(context, "forRecordCleanup");
1745
+ const itemRefVar = nextVar(context, "forItemRef");
1746
+ const indexRefVar = nextVar(context, "forIndexRef");
1747
+ const recordStartVar = nextVar(context, "forRecordStart");
1748
+ const recordEndVar = nextVar(context, "forRecordEnd");
1749
+ const memoVar = compiledMemo ? nextVar(context, "forMemo") : undefined;
1750
+ const memoChangedVar = compiledMemo ? nextVar(context, "forMemoChanged") : undefined;
1751
+ emit(context, indent + 1, `const ${sourceVar} = unwrap(${compiledSource}) ?? [];`);
1752
+ emit(context, indent + 1, `const ${nextRecordsVar} = new Map();`);
1753
+ emit(context, indent + 1, `for (let ${indexVar} = 0; ${indexVar} < ${sourceVar}.length; ${indexVar} += 1) {`);
1754
+ emit(context, indent + 2, `const ${rawItemVar} = ${sourceVar}[${indexVar}];`);
1755
+ emit(context, indent + 2, `const ${rawIndexVar} = ${indexVar};`);
1756
+ emit(context, indent + 2, `const ${itemName} = ${rawItemVar};`);
1757
+ if (indexName) {
1758
+ emit(context, indent + 2, `const ${indexName} = ${rawIndexVar};`);
1759
+ }
1760
+ emit(context, indent + 2, `const ${keyVar} = unwrap(${compiledKey});`);
1761
+ if (compiledMemo && memoVar) {
1762
+ emit(context, indent + 2, `const ${memoVar} = ${compiledMemo};`);
1763
+ }
1764
+ emit(context, indent + 2, `let ${recordVar} = ${recordsVar}.get(${keyVar});`);
1765
+ emit(context, indent + 2, `if (!${recordVar}) {`);
1766
+ emit(context, indent + 3, `const ${recordCleanupVar} = [];`);
1767
+ emit(context, indent + 3, `const ${itemRefVar} = ref(${rawItemVar});`);
1768
+ if (indexName) {
1769
+ emit(context, indent + 3, `const ${indexRefVar} = ref(${rawIndexVar});`);
1770
+ }
1771
+ emit(context, indent + 3, `const ${recordStartVar} = document.createComment("for item");`);
1772
+ emit(context, indent + 3, `const ${recordEndVar} = document.createComment("/for item");`);
1773
+ emit(context, indent + 3, `${parentVar}.insertBefore(${recordStartVar}, ${endVar});`);
1774
+ emit(context, indent + 3, `${parentVar}.insertBefore(${recordEndVar}, ${endVar});`);
1775
+ emit(context, indent + 3, `{`);
1776
+ emit(context, indent + 4, `const ${itemName} = ${itemRefVar};`);
1777
+ if (indexName) {
1778
+ emit(context, indent + 4, `const ${indexName} = ${indexRefVar};`);
1779
+ }
1780
+ withTemplateRefMode(context, "array", () => {
1781
+ generateChildren(context, node.children, parentVar, recordCleanupVar, indent + 4, recordEndVar);
1782
+ });
1783
+ emit(context, indent + 4, `${recordVar} = { start: ${recordStartVar}, end: ${recordEndVar}, cleanups: ${recordCleanupVar}, item: ${itemRefVar}${indexName ? `, index: ${indexRefVar}` : ""}${compiledMemo && memoVar ? `, memo: ${memoVar}` : ""} };`);
1784
+ emit(context, indent + 3, `}`);
1785
+ emit(context, indent + 2, `} else {`);
1786
+ if (compiledMemo && memoVar && memoChangedVar) {
1787
+ emit(context, indent + 3, `const ${memoChangedVar} = !__mikuru_memoEqual(${recordVar}.memo, ${memoVar});`);
1788
+ emit(context, indent + 3, `if (${memoChangedVar}) {`);
1789
+ emit(context, indent + 4, `${recordVar}.memo = ${memoVar};`);
1790
+ emit(context, indent + 4, `${recordVar}.item.value = ${rawItemVar};`);
1791
+ if (indexName) {
1792
+ emit(context, indent + 4, `${recordVar}.index.value = ${rawIndexVar};`);
1793
+ }
1794
+ emit(context, indent + 3, "}");
1795
+ }
1796
+ else {
1797
+ emit(context, indent + 3, `${recordVar}.item.value = ${rawItemVar};`);
1798
+ if (indexName) {
1799
+ emit(context, indent + 3, `${recordVar}.index.value = ${rawIndexVar};`);
1800
+ }
1801
+ }
1802
+ emitMoveRangeBefore(context, indent + 3, `${recordVar}.start`, `${recordVar}.end`, parentVar, endVar);
1803
+ emit(context, indent + 2, `}`);
1804
+ emit(context, indent + 2, `${nextRecordsVar}.set(${keyVar}, ${recordVar});`);
1805
+ emit(context, indent + 1, `}`);
1806
+ emit(context, indent + 1, `for (const [${keyVar}, ${recordVar}] of ${recordsVar}) {`);
1807
+ emit(context, indent + 2, `if (!${nextRecordsVar}.has(${keyVar})) {`);
1808
+ emit(context, indent + 3, `__mikuru_runCleanup(${recordVar}.cleanups);`);
1809
+ emitRemoveRange(context, indent + 3, `${recordVar}.start`, `${recordVar}.end`);
1810
+ emit(context, indent + 2, `}`);
1811
+ emit(context, indent + 1, `}`);
1812
+ emit(context, indent + 1, `${recordsVar}.clear();`);
1813
+ emit(context, indent + 1, `for (const [${keyVar}, ${recordVar}] of ${nextRecordsVar}) {`);
1814
+ emit(context, indent + 2, `${recordsVar}.set(${keyVar}, ${recordVar});`);
1815
+ emit(context, indent + 1, `}`);
1816
+ emit(context, indent, "});");
1817
+ emit(context, indent, `${cleanupVar}.push(() => {`);
1818
+ emit(context, indent + 1, `${stopVar}();`);
1819
+ emit(context, indent + 1, `for (const ${recordVar} of ${recordsVar}.values()) {`);
1820
+ emit(context, indent + 2, `__mikuru_runCleanup(${recordVar}.cleanups);`);
1821
+ emit(context, indent + 1, `}`);
1822
+ emit(context, indent + 1, `${recordsVar}.clear();`);
1823
+ emit(context, indent, "});");
1824
+ return startVar;
1825
+ }
1699
1826
  function generateKeyedFor(context, node, parentVar, cleanupVar, indent, itemName, indexName, sourceExpression, keyExpression, beforeVar, transitionVar) {
1700
1827
  const startVar = nextVar(context, "forStart");
1701
1828
  const endVar = nextVar(context, "forEnd");
@@ -1807,6 +1934,31 @@ function emitRemoveBetween(context, indent, startVar, endVar) {
1807
1934
  emit(context, indent + 1, `${currentVar} = ${nextVarName};`);
1808
1935
  emit(context, indent, "}");
1809
1936
  }
1937
+ function emitRemoveRange(context, indent, startExpression, endExpression) {
1938
+ const currentVar = nextVar(context, "current");
1939
+ const nextVarName = nextVar(context, "next");
1940
+ emit(context, indent, `let ${currentVar} = ${startExpression};`);
1941
+ emit(context, indent, `while (${currentVar}) {`);
1942
+ emit(context, indent + 1, `const ${nextVarName} = ${currentVar}.nextSibling;`);
1943
+ emit(context, indent + 1, `__mikuru_removeNode(${currentVar});`);
1944
+ emit(context, indent + 1, `if (${currentVar} === ${endExpression}) { break; }`);
1945
+ emit(context, indent + 1, `${currentVar} = ${nextVarName};`);
1946
+ emit(context, indent, "}");
1947
+ }
1948
+ function emitMoveRangeBefore(context, indent, startExpression, endExpression, parentVar, beforeExpression) {
1949
+ const fragmentVar = nextVar(context, "fragment");
1950
+ const currentVar = nextVar(context, "current");
1951
+ const nextVarName = nextVar(context, "next");
1952
+ emit(context, indent, `const ${fragmentVar} = document.createDocumentFragment();`);
1953
+ emit(context, indent, `let ${currentVar} = ${startExpression};`);
1954
+ emit(context, indent, `while (${currentVar}) {`);
1955
+ emit(context, indent + 1, `const ${nextVarName} = ${currentVar}.nextSibling;`);
1956
+ emit(context, indent + 1, `${fragmentVar}.appendChild(${currentVar});`);
1957
+ emit(context, indent + 1, `if (${currentVar} === ${endExpression}) { break; }`);
1958
+ emit(context, indent + 1, `${currentVar} = ${nextVarName};`);
1959
+ emit(context, indent, "}");
1960
+ emit(context, indent, `${parentVar}.insertBefore(${fragmentVar}, ${beforeExpression});`);
1961
+ }
1810
1962
  function emitTransitionRegistration(context, nodeVar, transitionVar, indent) {
1811
1963
  emit(context, indent, `if (${nodeVar}?.nodeType === 1) {`);
1812
1964
  emit(context, indent + 1, `${nodeVar}.__mikuru_transition = ${transitionVar};`);
@@ -2558,7 +2710,7 @@ function toExpressionContext(context, location) {
2558
2710
  function withoutAttr(node, name) {
2559
2711
  return withoutAttrs(node, [name]);
2560
2712
  }
2561
- function withoutForAttrs(node) {
2713
+ export function withoutForAttrs(node) {
2562
2714
  return withoutAttrs(node, ["v-for", "key", ":key", "v-bind:key", "v-memo", "v-once"]);
2563
2715
  }
2564
2716
  function withoutAttrs(node, names) {
@@ -3728,7 +3880,7 @@ function quote(value) {
3728
3880
  function quotePropertyName(value) {
3729
3881
  return /^[A-Za-z_$][\w$]*$/.test(value) ? value : quote(value);
3730
3882
  }
3731
- function createScopeAttr(descriptor) {
3883
+ export function createScopeAttr(descriptor) {
3732
3884
  return `data-mikuru-scope-${hash(`${descriptor.filename ?? ""}\n${descriptor.style ?? ""}`)}`;
3733
3885
  }
3734
3886
  function scopeCssSelectors(css, scopeAttr) {