meno-astro 0.1.4 → 0.1.6

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.
@@ -27,6 +27,7 @@ function resolveMappingsInFlat(style2, props) {
27
27
  if (resolved !== void 0) out[prop] = resolved;
28
28
  continue;
29
29
  }
30
+ if (typeof value === "string" && value.includes("{{")) continue;
30
31
  out[prop] = value;
31
32
  }
32
33
  return out;
@@ -77,4 +78,4 @@ export {
77
78
  computeClassName,
78
79
  style
79
80
  };
80
- //# sourceMappingURL=chunk-6VWPGRPL.js.map
81
+ //# sourceMappingURL=chunk-BYV2GH6J.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../lib/runtime/style.ts"],
4
- "sourcesContent": ["/**\n * meno-astro \u2014 `style()` runtime resolver + CSS collector (proof-of-concept).\n *\n * Emitted `.astro` markup styles every node with `class={style(styleObject[, meta])}`\n * (see `dialect/emit/emitNode.ts:105-124`). The `styleObject` is a Meno\n * `StyleObject` / `ResponsiveStyleObject` \u2014 `{ base, tablet, mobile }` where any value\n * may be a prop-binding `{ _mapping: true, prop, values }`. The optional `meta` carries\n * `interactive` (hover/etc. state rules), `label`, and `genClass`.\n *\n * This module is the runtime side of that contract:\n * 1. `style()` returns the element's class name (the `class={...}` value), and\n * 2. side-effects the generated CSS into a module-level collector that a future\n * `BaseLayout`/integration will flush into a `<style>` tag.\n *\n * \u2500\u2500 Why a props argument (the \"option A\" design being proven here) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * A `_mapping` value resolves against the *host component's prop values*, but the\n * emitted `style(styleObject, meta)` call does NOT carry them. Option A threads a\n * `props` scope into the call \u2014 `style(styleObject, props, meta)` \u2014 and resolves each\n * `_mapping` to a concrete CSS value *before* generating CSS. This file implements and\n * unit-tests exactly that. (The emitter change that actually passes `props` at the call\n * site is the *next* step, gated on this PoC \u2014 it is intentionally NOT done here.)\n *\n * \u2500\u2500 Reuse, not reinvention \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * All CSS generation is meno-core's, the same code the JSON runtime's SSR uses:\n * - `generateInteractiveCSS` \u2014 turns a list of selector rules (base + `:hover` + \u2026)\n * into CSS, with full responsive `base`/`tablet`/`mobile` \u2192 `@media` handling and\n * the `styleObjectToCSS` property serializer. We model the base style as one rule\n * with an empty postfix (`.<class> { \u2026 }`) and append the interactive rules, so a\n * single call covers base + responsive + interactive, all scoped to one class.\n * - `DEFAULT_BREAKPOINTS` \u2014 the project's breakpoint media-query thresholds\n * (tablet 1024px, mobile 540px).\n * - `shortHash` \u2014 meno-core's deterministic djb2 hash, so identical styleObjects\n * dedupe to the same class name.\n * - `isStyleMapping` \u2014 the `_mapping` type guard; mapping *resolution* mirrors\n * meno-core's `resolveExtractedMappings` (`mapping.values[String(propValue)]`).\n *\n * \u2500\u2500 Collector design + the per-render-isolation caveat \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * The collector is a module-level `Map` keyed by class name (so repeated `style()`\n * calls for the same class are naturally de-duplicated within a render). `style()`\n * appends; `flushCollectedStyles()` concatenates; `resetStyleCollector()` clears.\n *\n * PRODUCTION HARDENING (out of scope for this PoC): SSR is concurrent, so a module-\n * level sink races across overlapping renders. The production form scopes the\n * collector to the async call tree with `AsyncLocalStorage` (node:async_hooks) \u2014 the\n * exact pattern already used for the locale context in `runtime/i18n.ts`'s\n * `runWithLocale`. A `BaseLayout` would `runWithStyleCollector(() => \u2026)` around the\n * render and flush the per-render sink into a `<style>`. The module-level + resettable\n * collector here is sufficient to *prove the resolver* under unit tests.\n */\n\n// Narrow `meno-core/shared/*` subpath imports (the same convention `runtime/i18n.ts`\n// uses) rather than the broad `meno-core` barrel \u2014 the barrel re-exports the whole\n// shared surface, dragging unrelated modules (and their latent type errors) into this\n// package's type-check graph. We only need the style pipeline.\nimport {\n DEFAULT_BREAKPOINTS,\n type BreakpointConfig,\n} from 'meno-core/shared';\nimport { generateInteractiveCSS } from 'meno-core/shared';\nimport { isStyleMapping } from 'meno-core/shared';\nimport { shortHash } from 'meno-core/shared';\n// The SAME forward mapper meno-core's JSON runtime (ComponentBuilder) uses \u2014 so the\n// emitted class names are byte-identical to Meno core. The utility CSS itself is\n// generated at BUILD time by the meno() integration (Astro renders <head> before\n// <body>, so a runtime collector would always be empty); style() only returns names.\nimport { responsiveStylesToClasses } from 'meno-core/shared';\nimport type {\n StyleObject,\n StyleValue,\n ResponsiveStyleObject,\n InteractiveStyles,\n InteractiveStyleRule,\n} from 'meno-core/shared/types';\n\n// ---------------------------------------------------------------------------\n// `meta` \u2014 the second/third argument shape emitted alongside the style object.\n// Mirrors `emitClassAttr` in dialect/emit/emitNode.ts.\n// ---------------------------------------------------------------------------\n\n/** The `meta` payload carried by `class={style(styleObject, meta)}`. */\nexport interface StyleMeta {\n /** Hover/focus/state rules (`.element:hover { \u2026 }`, etc.). */\n interactive?: InteractiveStyles;\n /** The node's editor label \u2014 folded into the (still content-derived) class name. */\n label?: string;\n /** The node's `generateElementClass` flag (carried for parity; not load-bearing here). */\n genClass?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Module-level CSS collector. See the per-render-isolation caveat in the header.\n// Keyed by class name so duplicate `style()` calls within a render emit once.\n// ---------------------------------------------------------------------------\n\nconst collected = new Map<string, string>();\n\n/** Drain the collector, returning all accumulated CSS as a single string (insertion order). */\nexport function flushCollectedStyles(): string {\n return Array.from(collected.values()).filter(Boolean).join('\\n');\n}\n\n/** Clear the collector. Call between renders (the PoC stand-in for per-render isolation). */\nexport function resetStyleCollector(): void {\n collected.clear();\n}\n\n// ---------------------------------------------------------------------------\n// `_mapping` (prop-binding) resolution \u2014 the option-A proof.\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a single `_mapping` value against `props`, returning the concrete CSS value\n * or `undefined` when it cannot be resolved (no props, prop unset, or value not in the\n * mapping's table). Semantics mirror meno-core's `resolveExtractedMappings`:\n * `mapping.values[String(props[mapping.prop])]`.\n */\nfunction resolveMappingValue(\n mapping: { prop: string; values: Record<string, string | number> },\n props: Record<string, unknown> | undefined,\n): string | number | undefined {\n if (!props) return undefined;\n const propValue = props[mapping.prop];\n if (propValue === undefined || propValue === null) return undefined;\n const resolved = mapping.values[String(propValue)];\n return resolved === undefined ? undefined : resolved;\n}\n\n/**\n * Return a copy of a flat StyleObject with every `_mapping` value replaced by its\n * prop-resolved concrete value. Unresolvable mappings are dropped (graceful\n * degradation \u2014 a missing prop must never throw, and an unresolved property simply\n * isn't emitted, falling back to the cascade/UA default). Plain values pass through.\n */\nfunction resolveMappingsInFlat(\n style: StyleObject,\n props: Record<string, unknown> | undefined,\n): StyleObject {\n const out: StyleObject = {};\n for (const [prop, value] of Object.entries(style)) {\n if (isStyleMapping(value)) {\n const resolved = resolveMappingValue(value, props);\n if (resolved !== undefined) out[prop] = resolved;\n // else: drop \u2014 unresolved mapping, no rule emitted.\n continue;\n }\n out[prop] = value;\n }\n return out;\n}\n\n/** True when a StyleValue is the responsive `{ base/tablet/mobile }` shape. */\nfunction isResponsive(style: StyleValue): style is ResponsiveStyleObject {\n return (\n typeof style === 'object' &&\n style !== null &&\n ('base' in style || 'tablet' in style || 'mobile' in style)\n );\n}\n\n/**\n * Resolve `_mapping` values across a whole StyleValue (flat or responsive), per\n * breakpoint, against `props`. The result is a plain StyleValue with no `_mapping`\n * objects remaining \u2014 ready for meno-core's CSS generator.\n */\nexport function resolveMappingsInStyle(\n style: StyleValue,\n props: Record<string, unknown> | undefined,\n): StyleValue {\n if (isResponsive(style)) {\n const out: ResponsiveStyleObject = {};\n for (const [bp, bpStyle] of Object.entries(style)) {\n if (!bpStyle) continue;\n out[bp] = resolveMappingsInFlat(bpStyle, props);\n }\n return out;\n }\n return resolveMappingsInFlat(style as StyleObject, props);\n}\n\n// ---------------------------------------------------------------------------\n// Deterministic class name.\n// ---------------------------------------------------------------------------\n\n/** Lowercase + CSS-safe a label fragment for use in a class name. */\nfunction sanitizeLabel(label: string): string {\n return label\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '_')\n .replace(/_+/g, '_')\n .replace(/^_|_$/g, '');\n}\n\n/**\n * Compute a deterministic class name from the *resolved* style payload (base style +\n * interactive rules) plus the optional label. Identical inputs \u21D2 identical class, so\n * two `style()` calls with the same styleObject dedupe to one CSS rule. The hash uses\n * meno-core's `shortHash` (djb2). A label prefix is added for human-readable selectors\n * but the hash still keys dedup, so distinct styles never collide on label alone.\n */\nexport function computeClassName(\n resolvedBase: StyleValue,\n resolvedInteractive: InteractiveStyles,\n label: string | undefined,\n): string {\n const fingerprint = JSON.stringify({ b: resolvedBase, i: resolvedInteractive });\n const hash = shortHash(fingerprint);\n const prefix = label ? sanitizeLabel(label) : '';\n return prefix ? `m_${prefix}_${hash}` : `m_${hash}`;\n}\n\n// ---------------------------------------------------------------------------\n// style() \u2014 the emitter-facing resolver.\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a Meno style payload to a CSS class name, side-effecting the generated CSS\n * into the module-level collector.\n *\n * @param styleObject The node's style \u2014 flat `StyleObject` or responsive\n * `{ base, tablet, mobile }`. Values may be `_mapping` prop bindings.\n * @param props The host component's resolved prop values, used to resolve\n * `_mapping` bindings (the option-A contract). Optional: a style\n * with a `_mapping` but no `props` degrades gracefully (the bound\n * property is omitted rather than throwing).\n * @param meta Optional `{ interactive, label, genClass }` \u2014 interactive state\n * rules and label, as emitted.\n * @returns The element's CSS class name (the `class={style(...)}` value).\n */\nexport function style(\n styleObject: StyleValue | null | undefined,\n props?: Record<string, unknown>,\n meta?: StyleMeta,\n): string {\n const base = styleObject ?? {};\n\n // 1. Resolve prop-bound `_mapping` values to concrete CSS values using the host\n // component's props (the same resolution meno-core does at render).\n const resolvedBase = resolveMappingsInStyle(base, props);\n\n // 2. Base styles \u2192 meno-core utility classes (byte-identical to the JSON runtime).\n const classes = responsiveStylesToClasses(resolvedBase);\n\n // 3. Interactive (`:hover`, \u2026) styles aren't expressible as utility classes (they're\n // states), so meno-core scopes them to an element-specific class. Resolve their\n // `_mapping`s the same way and emit a DETERMINISTIC class so the build-time CSS\n // scan generates the matching `.<class>:hover { \u2026 }` rule. No interactive \u21D2 no\n // extra class.\n const resolvedInteractive: InteractiveStyles = (meta?.interactive ?? []).map(\n (rule): InteractiveStyleRule => ({\n ...rule,\n style: resolveMappingsInStyle(rule.style, props),\n }),\n );\n if (resolvedInteractive.length > 0) {\n classes.push(computeClassName({}, resolvedInteractive, meta?.label));\n }\n\n return classes.join(' ');\n}\n"],
5
- "mappings": ";;;;;;;AA8FA,IAAM,YAAY,oBAAI,IAAoB;AAGnC,SAAS,uBAA+B;AAC7C,SAAO,MAAM,KAAK,UAAU,OAAO,CAAC,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AACjE;AAGO,SAAS,sBAA4B;AAC1C,YAAU,MAAM;AAClB;AAYA,SAAS,oBACP,SACA,OAC6B;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,YAAY,MAAM,QAAQ,IAAI;AACpC,MAAI,cAAc,UAAa,cAAc,KAAM,QAAO;AAC1D,QAAM,WAAW,QAAQ,OAAO,OAAO,SAAS,CAAC;AACjD,SAAO,aAAa,SAAY,SAAY;AAC9C;AAQA,SAAS,sBACPA,QACA,OACa;AACb,QAAM,MAAmB,CAAC;AAC1B,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQA,MAAK,GAAG;AACjD,QAAI,eAAe,KAAK,GAAG;AACzB,YAAM,WAAW,oBAAoB,OAAO,KAAK;AACjD,UAAI,aAAa,OAAW,KAAI,IAAI,IAAI;AAExC;AAAA,IACF;AACA,QAAI,IAAI,IAAI;AAAA,EACd;AACA,SAAO;AACT;AAGA,SAAS,aAAaA,QAAmD;AACvE,SACE,OAAOA,WAAU,YACjBA,WAAU,SACT,UAAUA,UAAS,YAAYA,UAAS,YAAYA;AAEzD;AAOO,SAAS,uBACdA,QACA,OACY;AACZ,MAAI,aAAaA,MAAK,GAAG;AACvB,UAAM,MAA6B,CAAC;AACpC,eAAW,CAAC,IAAI,OAAO,KAAK,OAAO,QAAQA,MAAK,GAAG;AACjD,UAAI,CAAC,QAAS;AACd,UAAI,EAAE,IAAI,sBAAsB,SAAS,KAAK;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AACA,SAAO,sBAAsBA,QAAsB,KAAK;AAC1D;AAOA,SAAS,cAAc,OAAuB;AAC5C,SAAO,MACJ,YAAY,EACZ,QAAQ,gBAAgB,GAAG,EAC3B,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE;AACzB;AASO,SAAS,iBACd,cACA,qBACA,OACQ;AACR,QAAM,cAAc,KAAK,UAAU,EAAE,GAAG,cAAc,GAAG,oBAAoB,CAAC;AAC9E,QAAM,OAAO,UAAU,WAAW;AAClC,QAAM,SAAS,QAAQ,cAAc,KAAK,IAAI;AAC9C,SAAO,SAAS,KAAK,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI;AACnD;AAoBO,SAAS,MACd,aACA,OACA,MACQ;AACR,QAAM,OAAO,eAAe,CAAC;AAI7B,QAAM,eAAe,uBAAuB,MAAM,KAAK;AAGvD,QAAM,UAAU,0BAA0B,YAAY;AAOtD,QAAM,uBAA0C,MAAM,eAAe,CAAC,GAAG;AAAA,IACvE,CAAC,UAAgC;AAAA,MAC/B,GAAG;AAAA,MACH,OAAO,uBAAuB,KAAK,OAAO,KAAK;AAAA,IACjD;AAAA,EACF;AACA,MAAI,oBAAoB,SAAS,GAAG;AAClC,YAAQ,KAAK,iBAAiB,CAAC,GAAG,qBAAqB,MAAM,KAAK,CAAC;AAAA,EACrE;AAEA,SAAO,QAAQ,KAAK,GAAG;AACzB;",
4
+ "sourcesContent": ["/**\n * meno-astro \u2014 `style()` runtime resolver + CSS collector (proof-of-concept).\n *\n * Emitted `.astro` markup styles every node with `class={style(styleObject[, meta])}`\n * (see `dialect/emit/emitNode.ts:105-124`). The `styleObject` is a Meno\n * `StyleObject` / `ResponsiveStyleObject` \u2014 `{ base, tablet, mobile }` where any value\n * may be a prop-binding `{ _mapping: true, prop, values }`. The optional `meta` carries\n * `interactive` (hover/etc. state rules), `label`, and `genClass`.\n *\n * This module is the runtime side of that contract:\n * 1. `style()` returns the element's class name (the `class={...}` value), and\n * 2. side-effects the generated CSS into a module-level collector that a future\n * `BaseLayout`/integration will flush into a `<style>` tag.\n *\n * \u2500\u2500 Why a props argument (the \"option A\" design being proven here) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * A `_mapping` value resolves against the *host component's prop values*, but the\n * emitted `style(styleObject, meta)` call does NOT carry them. Option A threads a\n * `props` scope into the call \u2014 `style(styleObject, props, meta)` \u2014 and resolves each\n * `_mapping` to a concrete CSS value *before* generating CSS. This file implements and\n * unit-tests exactly that. (The emitter change that actually passes `props` at the call\n * site is the *next* step, gated on this PoC \u2014 it is intentionally NOT done here.)\n *\n * \u2500\u2500 Reuse, not reinvention \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * All CSS generation is meno-core's, the same code the JSON runtime's SSR uses:\n * - `generateInteractiveCSS` \u2014 turns a list of selector rules (base + `:hover` + \u2026)\n * into CSS, with full responsive `base`/`tablet`/`mobile` \u2192 `@media` handling and\n * the `styleObjectToCSS` property serializer. We model the base style as one rule\n * with an empty postfix (`.<class> { \u2026 }`) and append the interactive rules, so a\n * single call covers base + responsive + interactive, all scoped to one class.\n * - `DEFAULT_BREAKPOINTS` \u2014 the project's breakpoint media-query thresholds\n * (tablet 1024px, mobile 540px).\n * - `shortHash` \u2014 meno-core's deterministic djb2 hash, so identical styleObjects\n * dedupe to the same class name.\n * - `isStyleMapping` \u2014 the `_mapping` type guard; mapping *resolution* mirrors\n * meno-core's `resolveExtractedMappings` (`mapping.values[String(propValue)]`).\n *\n * \u2500\u2500 Collector design + the per-render-isolation caveat \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * The collector is a module-level `Map` keyed by class name (so repeated `style()`\n * calls for the same class are naturally de-duplicated within a render). `style()`\n * appends; `flushCollectedStyles()` concatenates; `resetStyleCollector()` clears.\n *\n * PRODUCTION HARDENING (out of scope for this PoC): SSR is concurrent, so a module-\n * level sink races across overlapping renders. The production form scopes the\n * collector to the async call tree with `AsyncLocalStorage` (node:async_hooks) \u2014 the\n * exact pattern already used for the locale context in `runtime/i18n.ts`'s\n * `runWithLocale`. A `BaseLayout` would `runWithStyleCollector(() => \u2026)` around the\n * render and flush the per-render sink into a `<style>`. The module-level + resettable\n * collector here is sufficient to *prove the resolver* under unit tests.\n */\n\n// Narrow `meno-core/shared/*` subpath imports (the same convention `runtime/i18n.ts`\n// uses) rather than the broad `meno-core` barrel \u2014 the barrel re-exports the whole\n// shared surface, dragging unrelated modules (and their latent type errors) into this\n// package's type-check graph. We only need the style pipeline.\nimport {\n DEFAULT_BREAKPOINTS,\n type BreakpointConfig,\n} from 'meno-core/shared';\nimport { generateInteractiveCSS } from 'meno-core/shared';\nimport { isStyleMapping } from 'meno-core/shared';\nimport { shortHash } from 'meno-core/shared';\n// The SAME forward mapper meno-core's JSON runtime (ComponentBuilder) uses \u2014 so the\n// emitted class names are byte-identical to Meno core. The utility CSS itself is\n// generated at BUILD time by the meno() integration (Astro renders <head> before\n// <body>, so a runtime collector would always be empty); style() only returns names.\nimport { responsiveStylesToClasses } from 'meno-core/shared';\nimport type {\n StyleObject,\n StyleValue,\n ResponsiveStyleObject,\n InteractiveStyles,\n InteractiveStyleRule,\n} from 'meno-core/shared/types';\n\n// ---------------------------------------------------------------------------\n// `meta` \u2014 the second/third argument shape emitted alongside the style object.\n// Mirrors `emitClassAttr` in dialect/emit/emitNode.ts.\n// ---------------------------------------------------------------------------\n\n/** The `meta` payload carried by `class={style(styleObject, meta)}`. */\nexport interface StyleMeta {\n /** Hover/focus/state rules (`.element:hover { \u2026 }`, etc.). */\n interactive?: InteractiveStyles;\n /** The node's editor label \u2014 folded into the (still content-derived) class name. */\n label?: string;\n /** The node's `generateElementClass` flag (carried for parity; not load-bearing here). */\n genClass?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Module-level CSS collector. See the per-render-isolation caveat in the header.\n// Keyed by class name so duplicate `style()` calls within a render emit once.\n// ---------------------------------------------------------------------------\n\nconst collected = new Map<string, string>();\n\n/** Drain the collector, returning all accumulated CSS as a single string (insertion order). */\nexport function flushCollectedStyles(): string {\n return Array.from(collected.values()).filter(Boolean).join('\\n');\n}\n\n/** Clear the collector. Call between renders (the PoC stand-in for per-render isolation). */\nexport function resetStyleCollector(): void {\n collected.clear();\n}\n\n// ---------------------------------------------------------------------------\n// `_mapping` (prop-binding) resolution \u2014 the option-A proof.\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a single `_mapping` value against `props`, returning the concrete CSS value\n * or `undefined` when it cannot be resolved (no props, prop unset, or value not in the\n * mapping's table). Semantics mirror meno-core's `resolveExtractedMappings`:\n * `mapping.values[String(props[mapping.prop])]`.\n */\nfunction resolveMappingValue(\n mapping: { prop: string; values: Record<string, string | number> },\n props: Record<string, unknown> | undefined,\n): string | number | undefined {\n if (!props) return undefined;\n const propValue = props[mapping.prop];\n if (propValue === undefined || propValue === null) return undefined;\n const resolved = mapping.values[String(propValue)];\n return resolved === undefined ? undefined : resolved;\n}\n\n/**\n * Return a copy of a flat StyleObject with every `_mapping` value replaced by its\n * prop-resolved concrete value. Unresolvable mappings are dropped (graceful\n * degradation \u2014 a missing prop must never throw, and an unresolved property simply\n * isn't emitted, falling back to the cascade/UA default). Plain values pass through.\n */\nfunction resolveMappingsInFlat(\n style: StyleObject,\n props: Record<string, unknown> | undefined,\n): StyleObject {\n const out: StyleObject = {};\n for (const [prop, value] of Object.entries(style)) {\n if (isStyleMapping(value)) {\n const resolved = resolveMappingValue(value, props);\n if (resolved !== undefined) out[prop] = resolved;\n // else: drop \u2014 unresolved mapping, no rule emitted.\n continue;\n }\n // A `{{template}}` value (e.g. `gap: \"{{gap}}px\"`) is per-instance and can't be a\n // static utility class \u2014 the emitter renders it via an inline `style` attr instead\n // (see emitInlineStyleAttr). Drop it here so no broken `.<hash> { gap: {{gap}}px }`\n // class is generated. Parity with meno-core's static/dynamic style split.\n if (typeof value === 'string' && value.includes('{{')) continue;\n out[prop] = value;\n }\n return out;\n}\n\n/** True when a StyleValue is the responsive `{ base/tablet/mobile }` shape. */\nfunction isResponsive(style: StyleValue): style is ResponsiveStyleObject {\n return (\n typeof style === 'object' &&\n style !== null &&\n ('base' in style || 'tablet' in style || 'mobile' in style)\n );\n}\n\n/**\n * Resolve `_mapping` values across a whole StyleValue (flat or responsive), per\n * breakpoint, against `props`. The result is a plain StyleValue with no `_mapping`\n * objects remaining \u2014 ready for meno-core's CSS generator.\n */\nexport function resolveMappingsInStyle(\n style: StyleValue,\n props: Record<string, unknown> | undefined,\n): StyleValue {\n if (isResponsive(style)) {\n const out: ResponsiveStyleObject = {};\n for (const [bp, bpStyle] of Object.entries(style)) {\n if (!bpStyle) continue;\n out[bp] = resolveMappingsInFlat(bpStyle, props);\n }\n return out;\n }\n return resolveMappingsInFlat(style as StyleObject, props);\n}\n\n// ---------------------------------------------------------------------------\n// Deterministic class name.\n// ---------------------------------------------------------------------------\n\n/** Lowercase + CSS-safe a label fragment for use in a class name. */\nfunction sanitizeLabel(label: string): string {\n return label\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '_')\n .replace(/_+/g, '_')\n .replace(/^_|_$/g, '');\n}\n\n/**\n * Compute a deterministic class name from the *resolved* style payload (base style +\n * interactive rules) plus the optional label. Identical inputs \u21D2 identical class, so\n * two `style()` calls with the same styleObject dedupe to one CSS rule. The hash uses\n * meno-core's `shortHash` (djb2). A label prefix is added for human-readable selectors\n * but the hash still keys dedup, so distinct styles never collide on label alone.\n */\nexport function computeClassName(\n resolvedBase: StyleValue,\n resolvedInteractive: InteractiveStyles,\n label: string | undefined,\n): string {\n const fingerprint = JSON.stringify({ b: resolvedBase, i: resolvedInteractive });\n const hash = shortHash(fingerprint);\n const prefix = label ? sanitizeLabel(label) : '';\n return prefix ? `m_${prefix}_${hash}` : `m_${hash}`;\n}\n\n// ---------------------------------------------------------------------------\n// style() \u2014 the emitter-facing resolver.\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a Meno style payload to a CSS class name, side-effecting the generated CSS\n * into the module-level collector.\n *\n * @param styleObject The node's style \u2014 flat `StyleObject` or responsive\n * `{ base, tablet, mobile }`. Values may be `_mapping` prop bindings.\n * @param props The host component's resolved prop values, used to resolve\n * `_mapping` bindings (the option-A contract). Optional: a style\n * with a `_mapping` but no `props` degrades gracefully (the bound\n * property is omitted rather than throwing).\n * @param meta Optional `{ interactive, label, genClass }` \u2014 interactive state\n * rules and label, as emitted.\n * @returns The element's CSS class name (the `class={style(...)}` value).\n */\nexport function style(\n styleObject: StyleValue | null | undefined,\n props?: Record<string, unknown>,\n meta?: StyleMeta,\n): string {\n const base = styleObject ?? {};\n\n // 1. Resolve prop-bound `_mapping` values to concrete CSS values using the host\n // component's props (the same resolution meno-core does at render).\n const resolvedBase = resolveMappingsInStyle(base, props);\n\n // 2. Base styles \u2192 meno-core utility classes (byte-identical to the JSON runtime).\n const classes = responsiveStylesToClasses(resolvedBase);\n\n // 3. Interactive (`:hover`, \u2026) styles aren't expressible as utility classes (they're\n // states), so meno-core scopes them to an element-specific class. Resolve their\n // `_mapping`s the same way and emit a DETERMINISTIC class so the build-time CSS\n // scan generates the matching `.<class>:hover { \u2026 }` rule. No interactive \u21D2 no\n // extra class.\n const resolvedInteractive: InteractiveStyles = (meta?.interactive ?? []).map(\n (rule): InteractiveStyleRule => ({\n ...rule,\n style: resolveMappingsInStyle(rule.style, props),\n }),\n );\n if (resolvedInteractive.length > 0) {\n classes.push(computeClassName({}, resolvedInteractive, meta?.label));\n }\n\n return classes.join(' ');\n}\n"],
5
+ "mappings": ";;;;;;;AA8FA,IAAM,YAAY,oBAAI,IAAoB;AAGnC,SAAS,uBAA+B;AAC7C,SAAO,MAAM,KAAK,UAAU,OAAO,CAAC,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AACjE;AAGO,SAAS,sBAA4B;AAC1C,YAAU,MAAM;AAClB;AAYA,SAAS,oBACP,SACA,OAC6B;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,YAAY,MAAM,QAAQ,IAAI;AACpC,MAAI,cAAc,UAAa,cAAc,KAAM,QAAO;AAC1D,QAAM,WAAW,QAAQ,OAAO,OAAO,SAAS,CAAC;AACjD,SAAO,aAAa,SAAY,SAAY;AAC9C;AAQA,SAAS,sBACPA,QACA,OACa;AACb,QAAM,MAAmB,CAAC;AAC1B,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQA,MAAK,GAAG;AACjD,QAAI,eAAe,KAAK,GAAG;AACzB,YAAM,WAAW,oBAAoB,OAAO,KAAK;AACjD,UAAI,aAAa,OAAW,KAAI,IAAI,IAAI;AAExC;AAAA,IACF;AAKA,QAAI,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,EAAG;AACvD,QAAI,IAAI,IAAI;AAAA,EACd;AACA,SAAO;AACT;AAGA,SAAS,aAAaA,QAAmD;AACvE,SACE,OAAOA,WAAU,YACjBA,WAAU,SACT,UAAUA,UAAS,YAAYA,UAAS,YAAYA;AAEzD;AAOO,SAAS,uBACdA,QACA,OACY;AACZ,MAAI,aAAaA,MAAK,GAAG;AACvB,UAAM,MAA6B,CAAC;AACpC,eAAW,CAAC,IAAI,OAAO,KAAK,OAAO,QAAQA,MAAK,GAAG;AACjD,UAAI,CAAC,QAAS;AACd,UAAI,EAAE,IAAI,sBAAsB,SAAS,KAAK;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AACA,SAAO,sBAAsBA,QAAsB,KAAK;AAC1D;AAOA,SAAS,cAAc,OAAuB;AAC5C,SAAO,MACJ,YAAY,EACZ,QAAQ,gBAAgB,GAAG,EAC3B,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE;AACzB;AASO,SAAS,iBACd,cACA,qBACA,OACQ;AACR,QAAM,cAAc,KAAK,UAAU,EAAE,GAAG,cAAc,GAAG,oBAAoB,CAAC;AAC9E,QAAM,OAAO,UAAU,WAAW;AAClC,QAAM,SAAS,QAAQ,cAAc,KAAK,IAAI;AAC9C,SAAO,SAAS,KAAK,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI;AACnD;AAoBO,SAAS,MACd,aACA,OACA,MACQ;AACR,QAAM,OAAO,eAAe,CAAC;AAI7B,QAAM,eAAe,uBAAuB,MAAM,KAAK;AAGvD,QAAM,UAAU,0BAA0B,YAAY;AAOtD,QAAM,uBAA0C,MAAM,eAAe,CAAC,GAAG;AAAA,IACvE,CAAC,UAAgC;AAAA,MAC/B,GAAG;AAAA,MACH,OAAO,uBAAuB,KAAK,OAAO,KAAK;AAAA,IACjD;AAAA,EACF;AACA,MAAI,oBAAoB,SAAS,GAAG;AAClC,YAAQ,KAAK,iBAAiB,CAAC,GAAG,qBAAqB,MAAM,KAAK,CAAC;AAAA,EACrE;AAEA,SAAO,QAAQ,KAAK,GAAG;AACzB;",
6
6
  "names": ["style"]
7
7
  }
@@ -136,6 +136,25 @@ function templateToExpr(s) {
136
136
  out += escapeBacktick(s.slice(last));
137
137
  return "`" + out + "`";
138
138
  }
139
+ function rewriteItemRefs(str, itemVar) {
140
+ return str.replace(TEMPLATE_RE, (_m, expr) => {
141
+ const rewritten = String(expr).replace(/\bitemIndex\b/g, `${itemVar}Index`).replace(/\bitemFirst\b/g, `${itemVar}First`).replace(/\bitemLast\b/g, `${itemVar}Last`).replace(/\bitem\b/g, itemVar);
142
+ return `{{${rewritten}}}`;
143
+ });
144
+ }
145
+ function rewriteItemRefsInTree(node, itemVar) {
146
+ if (typeof node === "string") return rewriteItemRefs(node, itemVar);
147
+ if (Array.isArray(node)) return node.map((n) => rewriteItemRefsInTree(n, itemVar));
148
+ if (node && typeof node === "object") {
149
+ const obj = node;
150
+ const out = {};
151
+ for (const [k, v] of Object.entries(obj)) {
152
+ out[k] = k === "children" && obj.type === "list" ? v : rewriteItemRefsInTree(v, itemVar);
153
+ }
154
+ return out;
155
+ }
156
+ return node;
157
+ }
139
158
  var NON_BINDING_ROOTS = /* @__PURE__ */ new Set([
140
159
  "Astro",
141
160
  "Math",
@@ -199,8 +218,31 @@ function emitAttr(name, value, ctx) {
199
218
  return `${name}={${value}}`;
200
219
  }
201
220
  if (value === null || value === void 0) return `${name}={null}`;
221
+ if (deepHasTemplate(value)) return `${name}={${serializeExprLiteral(value)}}`;
202
222
  return `${name}={${serializeLiteral(value, { indent: INDENT, width: ctx.width })}}`;
203
223
  }
224
+ function deepHasTemplate(value) {
225
+ if (typeof value === "string") return hasTemplate(value);
226
+ if (Array.isArray(value)) return value.some(deepHasTemplate);
227
+ if (value && typeof value === "object") return Object.values(value).some(deepHasTemplate);
228
+ return false;
229
+ }
230
+ function isBareIdent(key) {
231
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key);
232
+ }
233
+ function serializeExprLiteral(value) {
234
+ if (typeof value === "string") return hasTemplate(value) ? templateToExpr(value) : JSON.stringify(value);
235
+ if (value === null || value === void 0) return "null";
236
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
237
+ if (Array.isArray(value)) return `[${value.map(serializeExprLiteral).join(", ")}]`;
238
+ if (typeof value === "object") {
239
+ const entries = Object.entries(value).map(
240
+ ([k, v]) => `${isBareIdent(k) ? k : JSON.stringify(k)}: ${serializeExprLiteral(v)}`
241
+ );
242
+ return entries.length ? `{ ${entries.join(", ")} }` : "{}";
243
+ }
244
+ return "null";
245
+ }
204
246
  function emitClassAttr(node, ctx, extraMeta) {
205
247
  const meta = { ...extraMeta };
206
248
  if (node.interactiveStyles !== void 0) meta.interactive = node.interactiveStyles;
@@ -216,6 +258,30 @@ function emitClassAttr(node, ctx, extraMeta) {
216
258
  const metaLit = serializeLiteral(meta, { indent: INDENT, width: ctx.width });
217
259
  return `class={style(${styleLit}, ${propsArg ?? "undefined"}, ${metaLit})}`;
218
260
  }
261
+ function cssPropName(key) {
262
+ return key.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
263
+ }
264
+ function templatedStyleProps(style) {
265
+ if (!style || typeof style !== "object") return [];
266
+ const s = style;
267
+ const isResponsive = "base" in s || "tablet" in s || "mobile" in s;
268
+ const flat = isResponsive ? s.base : s;
269
+ if (!flat || typeof flat !== "object") return [];
270
+ const out = [];
271
+ for (const [k, v] of Object.entries(flat)) {
272
+ if (typeof v === "string" && v.includes("{{")) out.push([cssPropName(k), v]);
273
+ }
274
+ return out;
275
+ }
276
+ function emitInlineStyleAttr(node) {
277
+ const props = templatedStyleProps(node.style);
278
+ if (props.length === 0) return null;
279
+ const decls = props.map(([cssProp, val]) => {
280
+ const resolved = val.replace(TEMPLATE_RE, (_m, e) => "${" + String(e).trim() + "}");
281
+ return `${cssProp}: ${resolved}`;
282
+ });
283
+ return `style={\`${decls.join("; ")}\`}`;
284
+ }
219
285
  function hasStyleContent(style) {
220
286
  if (!style || typeof style !== "object") return false;
221
287
  for (const v of Object.values(style)) {
@@ -300,7 +366,7 @@ function renderHtml(node, ctx) {
300
366
  ctx.frontmatterConsts.push(`const ${varName} = ${tagToTemplateLiteral(tag)};`);
301
367
  tag = varName;
302
368
  }
303
- const attrs = [emitClassAttr(node, ctx), ...emitAttributes(node, ctx)].filter(Boolean);
369
+ const attrs = [emitClassAttr(node, ctx), emitInlineStyleAttr(node), ...emitAttributes(node, ctx)].filter(Boolean);
304
370
  const isVoid = VOID_ELEMENTS.has(String(node.tag).toLowerCase());
305
371
  const childBlocks = isVoid ? [] : emitChildrenList(node.children, ctx);
306
372
  return { kind: "element", markup: composeElement(tag, attrs, childBlocks, isVoid) };
@@ -320,6 +386,8 @@ function renderComponentInstance(node, ctx) {
320
386
  }
321
387
  const cls = emitClassAttr(node, ctx, hasStyleContent(node.style) ? { instance: true } : void 0);
322
388
  if (cls) attrs.push(cls);
389
+ const inlineStyle = emitInlineStyleAttr(node);
390
+ if (inlineStyle) attrs.push(inlineStyle);
323
391
  const childBlocks = emitChildrenList(node.children, ctx);
324
392
  return { kind: "element", markup: composeElement(tag, attrs, childBlocks) };
325
393
  }
@@ -334,6 +402,8 @@ function renderLink(node, ctx) {
334
402
  const attrs = [emitHref(node.href, ctx)];
335
403
  const cls = emitClassAttr(node, ctx);
336
404
  if (cls) attrs.push(cls);
405
+ const inlineStyle = emitInlineStyleAttr(node);
406
+ if (inlineStyle) attrs.push(inlineStyle);
337
407
  attrs.push(...emitAttributes(node, ctx));
338
408
  const childBlocks = emitChildrenList(node.children, ctx);
339
409
  return { kind: "element", markup: composeElement("Link", attrs.filter(Boolean), childBlocks) };
@@ -371,6 +441,8 @@ function renderEmbed(node, ctx) {
371
441
  }
372
442
  const cls = emitClassAttr(node, ctx);
373
443
  if (cls) attrs.push(cls);
444
+ const inlineStyle = emitInlineStyleAttr(node);
445
+ if (inlineStyle) attrs.push(inlineStyle);
374
446
  attrs.push(...emitAttributes(node, ctx));
375
447
  return { kind: "element", markup: composeElement("Embed", attrs, [], true) };
376
448
  }
@@ -406,8 +478,9 @@ function renderList(node, ctx) {
406
478
  const sourceType = node.sourceType ?? "prop";
407
479
  const itemVar = node.itemAs ?? (sourceType === "collection" ? singularize(node.source) : "item");
408
480
  const indexVar = `${itemVar}Index`;
481
+ const itemChildren = itemVar === "item" ? node.children : rewriteItemRefsInTree(node.children, itemVar);
409
482
  ctx.loopVars.push(itemVar);
410
- const childBlocks = emitChildrenList(node.children, ctx);
483
+ const childBlocks = emitChildrenList(itemChildren, ctx);
411
484
  ctx.loopVars.pop();
412
485
  const childRenderedAt2 = childBlocks.map((c) => shift(c, INDENT)).join("\n");
413
486
  if (sourceType === "collection") {
@@ -477,11 +550,11 @@ function renderNode(node, ctx) {
477
550
  default:
478
551
  rendered = { kind: "element", markup: `{/* meno:unknown ${JSON.stringify(node.type)} */}` };
479
552
  }
480
- return applyIf(node, rendered);
553
+ return applyIf(node, rendered, ctx);
481
554
  }
482
- function applyIf(node, rendered) {
555
+ function applyIf(node, rendered, ctx) {
483
556
  if (node.if === void 0 || node.if === true) return rendered;
484
- const cond = ifConditionExpr(node.if);
557
+ const cond = ifConditionExpr(node.if, ctx);
485
558
  if (rendered.kind === "element") {
486
559
  return { kind: "expr", expr: `${cond} && (
487
560
  ${shift(rendered.markup, INDENT)}
@@ -489,11 +562,13 @@ ${shift(rendered.markup, INDENT)}
489
562
  }
490
563
  return { kind: "expr", expr: `${cond} && (${rendered.expr})` };
491
564
  }
492
- function ifConditionExpr(cond) {
565
+ function ifConditionExpr(cond, ctx) {
493
566
  if (cond === false) return "false";
494
567
  if (cond === true) return "true";
495
568
  if (typeof cond === "string") return hasTemplate(cond) ? templateToExpr(cond) : cond;
496
- return `when(${serializeLiteral(cond)})`;
569
+ needRuntime(ctx, "when");
570
+ const condLit = serializeLiteral(cond);
571
+ return ctx.propsVar ? `when(${condLit}, ${ctx.propsVar})` : `when(${condLit})`;
497
572
  }
498
573
  function placeChild(rendered, indent) {
499
574
  if (rendered.kind === "element") return shift(rendered.markup, indent);
@@ -595,7 +670,7 @@ function emitPage(page, opts) {
595
670
  fm.push(buildGetStaticPaths(page.meta.cms));
596
671
  fm.push("");
597
672
  }
598
- fm.push(`export const meta = ${serializeLiteral(page.meta ?? {}, { indent: 0 })};`);
673
+ fm.push(`const meta = ${serializeLiteral(page.meta ?? {}, { indent: 0 })};`);
599
674
  if (ctx.frontmatterConsts.length) {
600
675
  fm.push("");
601
676
  fm.push(...ctx.frontmatterConsts);
@@ -684,6 +759,120 @@ ${body}
684
759
  ${styleBlock}${scriptBlock}`.replace(/\n+$/, "\n");
685
760
  }
686
761
 
762
+ // lib/dialect/parse/scan.ts
763
+ var CLOSERS = { "{": "}", "(": ")", "[": "]" };
764
+ function scanString(src, i) {
765
+ const q = src[i];
766
+ let j = i + 1;
767
+ while (j < src.length) {
768
+ if (src[j] === "\\") {
769
+ j += 2;
770
+ continue;
771
+ }
772
+ if (src[j] === q) return j + 1;
773
+ j++;
774
+ }
775
+ throw new Error(`scanString: unterminated string from ${i}`);
776
+ }
777
+ function scanTemplate(src, i) {
778
+ let j = i + 1;
779
+ while (j < src.length) {
780
+ if (src[j] === "\\") {
781
+ j += 2;
782
+ continue;
783
+ }
784
+ if (src[j] === "`") return j + 1;
785
+ if (src[j] === "$" && src[j + 1] === "{") {
786
+ j = scanBalanced(src, j + 1);
787
+ continue;
788
+ }
789
+ j++;
790
+ }
791
+ throw new Error(`scanTemplate: unterminated template from ${i}`);
792
+ }
793
+ function scanBalanced(src, i) {
794
+ if (!CLOSERS[src[i]]) throw new Error(`scanBalanced: no open delimiter at ${i} ("${src[i]}")`);
795
+ let depth = 0;
796
+ let j = i;
797
+ while (j < src.length) {
798
+ const c = src[j];
799
+ if (c === '"' || c === "'") {
800
+ j = scanString(src, j);
801
+ continue;
802
+ }
803
+ if (c === "`") {
804
+ j = scanTemplate(src, j);
805
+ continue;
806
+ }
807
+ if (c === "{" || c === "(" || c === "[") {
808
+ depth++;
809
+ j++;
810
+ continue;
811
+ }
812
+ if (c === "}" || c === ")" || c === "]") {
813
+ depth--;
814
+ j++;
815
+ if (depth === 0) return j;
816
+ continue;
817
+ }
818
+ j++;
819
+ }
820
+ throw new Error(`scanBalanced: unbalanced from ${i}`);
821
+ }
822
+ function findTrailingGroup(expr, openChar = "(") {
823
+ let j = 0;
824
+ while (j < expr.length) {
825
+ const c = expr[j];
826
+ if (c === '"' || c === "'") {
827
+ j = scanString(expr, j);
828
+ continue;
829
+ }
830
+ if (c === "`") {
831
+ j = scanTemplate(expr, j);
832
+ continue;
833
+ }
834
+ if (c === "{" || c === "(" || c === "[") {
835
+ const end = scanBalanced(expr, j);
836
+ if (c === openChar && end === expr.length) {
837
+ return { open: j, inner: expr.slice(j + 1, end - 1) };
838
+ }
839
+ j = end;
840
+ continue;
841
+ }
842
+ j++;
843
+ }
844
+ return null;
845
+ }
846
+ function splitTopLevel(src, sep) {
847
+ const parts = [];
848
+ let start = 0;
849
+ let j = 0;
850
+ while (j < src.length) {
851
+ const c = src[j];
852
+ if (c === '"' || c === "'") {
853
+ j = scanString(src, j);
854
+ continue;
855
+ }
856
+ if (c === "`") {
857
+ j = scanTemplate(src, j);
858
+ continue;
859
+ }
860
+ if (c === "{" || c === "(" || c === "[") {
861
+ j = scanBalanced(src, j);
862
+ continue;
863
+ }
864
+ if (c === sep) {
865
+ parts.push(src.slice(start, j));
866
+ start = j + 1;
867
+ j++;
868
+ continue;
869
+ }
870
+ j++;
871
+ }
872
+ parts.push(src.slice(start));
873
+ return parts.map((s) => s.trim());
874
+ }
875
+
687
876
  // lib/dialect/parse/parseLiteral.ts
688
877
  var WS = /* @__PURE__ */ new Set([" ", " ", "\r", "\n"]);
689
878
  var IDENT_START = /[A-Za-z_$]/;
@@ -766,137 +955,98 @@ function parseArray(src, i) {
766
955
  fail(src, j, 'expected "," or "]"');
767
956
  }
768
957
  }
769
- function parseValueAt(src, i) {
770
- i = skipWs(src, i);
771
- const c = src[i];
772
- if (c === "{") return parseObject(src, i);
773
- if (c === "[") return parseArray(src, i);
774
- if (c === '"') return parseString(src, i);
775
- if (src.startsWith("true", i)) return { value: true, end: i + 4 };
776
- if (src.startsWith("false", i)) return { value: false, end: i + 5 };
777
- if (src.startsWith("null", i)) return { value: null, end: i + 4 };
778
- if (c === "-" || c === "+" || c >= "0" && c <= "9") return parseNumber(src, i);
779
- return fail(src, i, `unexpected character "${c ?? "<eof>"}"`);
780
- }
781
- function parseLiteral(src) {
782
- const { value, end } = parseValueAt(src, 0);
783
- const rest = skipWs(src, end);
784
- if (rest !== src.length) fail(src, rest, "trailing content after literal");
785
- return value;
786
- }
787
-
788
- // lib/dialect/parse/scan.ts
789
- var CLOSERS = { "{": "}", "(": ")", "[": "]" };
790
- function scanString(src, i) {
791
- const q = src[i];
792
- let j = i + 1;
793
- while (j < src.length) {
794
- if (src[j] === "\\") {
795
- j += 2;
958
+ function reverseBacktickBody(content) {
959
+ let out = "";
960
+ let i = 0;
961
+ while (i < content.length) {
962
+ const c = content[i];
963
+ if (c === "\\") {
964
+ const n = content[i + 1];
965
+ out += n === "\\" ? "\\" : n === "`" ? "`" : n === "$" ? "$" : n;
966
+ i += 2;
796
967
  continue;
797
968
  }
798
- if (src[j] === q) return j + 1;
799
- j++;
969
+ if (c === "$" && content[i + 1] === "{") {
970
+ const end = scanBalanced(content, i + 1);
971
+ out += "{{" + content.slice(i + 2, end - 1).trim() + "}}";
972
+ i = end;
973
+ continue;
974
+ }
975
+ out += c;
976
+ i++;
800
977
  }
801
- throw new Error(`scanString: unterminated string from ${i}`);
978
+ return out;
802
979
  }
803
- function scanTemplate(src, i) {
980
+ function parseBacktick(src, i) {
804
981
  let j = i + 1;
805
982
  while (j < src.length) {
806
- if (src[j] === "\\") {
983
+ const c = src[j];
984
+ if (c === "\\") {
807
985
  j += 2;
808
986
  continue;
809
987
  }
810
- if (src[j] === "`") return j + 1;
811
- if (src[j] === "$" && src[j + 1] === "{") {
988
+ if (c === "`") {
989
+ j++;
990
+ break;
991
+ }
992
+ if (c === "$" && src[j + 1] === "{") {
812
993
  j = scanBalanced(src, j + 1);
813
994
  continue;
814
995
  }
815
996
  j++;
816
997
  }
817
- throw new Error(`scanTemplate: unterminated template from ${i}`);
998
+ return { value: reverseBacktickBody(src.slice(i + 1, j - 1)), end: j };
818
999
  }
819
- function scanBalanced(src, i) {
820
- if (!CLOSERS[src[i]]) throw new Error(`scanBalanced: no open delimiter at ${i} ("${src[i]}")`);
821
- let depth = 0;
1000
+ function parseExprToken(src, i) {
822
1001
  let j = i;
1002
+ let depth = 0;
823
1003
  while (j < src.length) {
824
1004
  const c = src[j];
825
- if (c === '"' || c === "'") {
826
- j = scanString(src, j);
827
- continue;
828
- }
829
- if (c === "`") {
830
- j = scanTemplate(src, j);
1005
+ if (c === '"' || c === "'" || c === "`") {
1006
+ const q = c;
1007
+ j++;
1008
+ while (j < src.length && src[j] !== q) {
1009
+ if (src[j] === "\\") j++;
1010
+ j++;
1011
+ }
1012
+ j++;
831
1013
  continue;
832
1014
  }
833
- if (c === "{" || c === "(" || c === "[") {
1015
+ if (c === "(" || c === "[" || c === "{") {
834
1016
  depth++;
835
1017
  j++;
836
1018
  continue;
837
1019
  }
838
- if (c === "}" || c === ")" || c === "]") {
1020
+ if (c === ")" || c === "]" || c === "}") {
1021
+ if (depth === 0) break;
839
1022
  depth--;
840
1023
  j++;
841
- if (depth === 0) return j;
842
1024
  continue;
843
1025
  }
1026
+ if (depth === 0 && c === ",") break;
844
1027
  j++;
845
1028
  }
846
- throw new Error(`scanBalanced: unbalanced from ${i}`);
1029
+ return { value: `{{${src.slice(i, j).trim()}}}`, end: j };
847
1030
  }
848
- function findTrailingGroup(expr, openChar = "(") {
849
- let j = 0;
850
- while (j < expr.length) {
851
- const c = expr[j];
852
- if (c === '"' || c === "'") {
853
- j = scanString(expr, j);
854
- continue;
855
- }
856
- if (c === "`") {
857
- j = scanTemplate(expr, j);
858
- continue;
859
- }
860
- if (c === "{" || c === "(" || c === "[") {
861
- const end = scanBalanced(expr, j);
862
- if (c === openChar && end === expr.length) {
863
- return { open: j, inner: expr.slice(j + 1, end - 1) };
864
- }
865
- j = end;
866
- continue;
867
- }
868
- j++;
869
- }
870
- return null;
1031
+ function parseValueAt(src, i) {
1032
+ i = skipWs(src, i);
1033
+ const c = src[i];
1034
+ if (c === "{") return parseObject(src, i);
1035
+ if (c === "[") return parseArray(src, i);
1036
+ if (c === '"') return parseString(src, i);
1037
+ if (c === "`") return parseBacktick(src, i);
1038
+ if (src.startsWith("true", i) && !IDENT_CHAR.test(src[i + 4] ?? "")) return { value: true, end: i + 4 };
1039
+ if (src.startsWith("false", i) && !IDENT_CHAR.test(src[i + 5] ?? "")) return { value: false, end: i + 5 };
1040
+ if (src.startsWith("null", i) && !IDENT_CHAR.test(src[i + 4] ?? "")) return { value: null, end: i + 4 };
1041
+ if (c === "-" || c === "+" || c >= "0" && c <= "9") return parseNumber(src, i);
1042
+ if (c !== void 0 && IDENT_START.test(c)) return parseExprToken(src, i);
1043
+ return fail(src, i, `unexpected character "${c ?? "<eof>"}"`);
871
1044
  }
872
- function splitTopLevel(src, sep) {
873
- const parts = [];
874
- let start = 0;
875
- let j = 0;
876
- while (j < src.length) {
877
- const c = src[j];
878
- if (c === '"' || c === "'") {
879
- j = scanString(src, j);
880
- continue;
881
- }
882
- if (c === "`") {
883
- j = scanTemplate(src, j);
884
- continue;
885
- }
886
- if (c === "{" || c === "(" || c === "[") {
887
- j = scanBalanced(src, j);
888
- continue;
889
- }
890
- if (c === sep) {
891
- parts.push(src.slice(start, j));
892
- start = j + 1;
893
- j++;
894
- continue;
895
- }
896
- j++;
897
- }
898
- parts.push(src.slice(start));
899
- return parts.map((s) => s.trim());
1045
+ function parseLiteral(src) {
1046
+ const { value, end } = parseValueAt(src, 0);
1047
+ const rest = skipWs(src, end);
1048
+ if (rest !== src.length) fail(src, rest, "trailing content after literal");
1049
+ return value;
900
1050
  }
901
1051
 
902
1052
  // lib/dialect/parse/callArgs.ts
@@ -962,7 +1112,7 @@ function reverseCondition(cond) {
962
1112
  const c = cond.trim();
963
1113
  if (c === "false") return false;
964
1114
  if (c === "true") return true;
965
- if (c.startsWith("when(")) return parseLiteral(callArgsOf(c));
1115
+ if (c.startsWith("when(")) return parseLiteral(splitTopLevel(callArgsOf(c), ",")[0]);
966
1116
  return `{{${c}}}`;
967
1117
  }
968
1118
 
@@ -1009,7 +1159,7 @@ function parseFrontmatter(code) {
1009
1159
  }
1010
1160
  const propsInterface = literalAfter(code, "resolveProps(Astro, ");
1011
1161
  const componentMeta = literalAfter(code, "const __meno = ") ?? {};
1012
- const meta = literalAfter(code, "export const meta = ");
1162
+ const meta = literalAfter(code, "const meta = ");
1013
1163
  const kind = code.includes("resolveProps(") ? "component" : "page";
1014
1164
  return { kind, ctx, meta, propsInterface, componentMeta };
1015
1165
  }
@@ -1215,7 +1365,7 @@ function elementToNode(tag, attrs, children, ctx) {
1215
1365
  if (ctx.tagConsts.has(tag)) {
1216
1366
  const node2 = { type: "node", tag: ctx.tagConsts.get(tag) };
1217
1367
  applyClass(node2, attrs);
1218
- const attributes2 = otherAttrs(attrs, ctx, /* @__PURE__ */ new Set(["class"]));
1368
+ const attributes2 = otherAttrs(attrs, ctx, /* @__PURE__ */ new Set(["class", "style"]));
1219
1369
  if (Object.keys(attributes2).length) node2.attributes = attributes2;
1220
1370
  if (children.length) node2.children = children;
1221
1371
  return node2;
@@ -1230,7 +1380,7 @@ function elementToNode(tag, attrs, children, ctx) {
1230
1380
  const href = attrs.find((a) => a.name === "href");
1231
1381
  if (href) node2.href = attrValue(href, ctx);
1232
1382
  applyClass(node2, attrs);
1233
- const rest = otherAttrs(attrs, ctx, /* @__PURE__ */ new Set(["href", "class"]));
1383
+ const rest = otherAttrs(attrs, ctx, /* @__PURE__ */ new Set(["href", "class", "style"]));
1234
1384
  if (Object.keys(rest).length) node2.attributes = rest;
1235
1385
  if (children.length) node2.children = children;
1236
1386
  return node2;
@@ -1240,7 +1390,7 @@ function elementToNode(tag, attrs, children, ctx) {
1240
1390
  const html = attrs.find((a) => a.name === "html");
1241
1391
  if (html) node2.html = attrValue(html, ctx);
1242
1392
  applyClass(node2, attrs);
1243
- const rest = otherAttrs(attrs, ctx, /* @__PURE__ */ new Set(["html", "class"]));
1393
+ const rest = otherAttrs(attrs, ctx, /* @__PURE__ */ new Set(["html", "class", "style"]));
1244
1394
  if (Object.keys(rest).length) node2.attributes = rest;
1245
1395
  return node2;
1246
1396
  }
@@ -1264,14 +1414,14 @@ function elementToNode(tag, attrs, children, ctx) {
1264
1414
  if (isComponent) {
1265
1415
  const node2 = { type: "component", component: tag };
1266
1416
  applyClass(node2, attrs);
1267
- const props = otherAttrs(attrs, ctx, /* @__PURE__ */ new Set(["class"]));
1417
+ const props = otherAttrs(attrs, ctx, /* @__PURE__ */ new Set(["class", "style"]));
1268
1418
  if (Object.keys(props).length) node2.props = props;
1269
1419
  if (children.length) node2.children = children;
1270
1420
  return node2;
1271
1421
  }
1272
1422
  const node = { type: "node", tag };
1273
1423
  applyClass(node, attrs);
1274
- const attributes = otherAttrs(attrs, ctx, /* @__PURE__ */ new Set(["class"]));
1424
+ const attributes = otherAttrs(attrs, ctx, /* @__PURE__ */ new Set(["class", "style"]));
1275
1425
  if (Object.keys(attributes).length) node.attributes = attributes;
1276
1426
  if (children.length) node.children = children;
1277
1427
  return node;
@@ -1530,4 +1680,4 @@ export {
1530
1680
  emit,
1531
1681
  parse
1532
1682
  };
1533
- //# sourceMappingURL=chunk-ACHLFXXY.js.map
1683
+ //# sourceMappingURL=chunk-HNGBRPTL.js.map