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.
- package/dist/chunks/{chunk-6VWPGRPL.js → chunk-BYV2GH6J.js} +2 -1
- package/dist/chunks/{chunk-6VWPGRPL.js.map → chunk-BYV2GH6J.js.map} +2 -2
- package/dist/chunks/{chunk-ACHLFXXY.js → chunk-HNGBRPTL.js} +265 -115
- package/dist/chunks/chunk-HNGBRPTL.js.map +7 -0
- package/dist/lib/dialect/index.js +1 -1
- package/dist/lib/index.js +10 -2
- package/dist/lib/index.js.map +2 -2
- package/dist/lib/integration/index.js +3 -2
- package/dist/lib/integration/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/chunks/chunk-ACHLFXXY.js.map +0 -7
|
@@ -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-
|
|
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;
|
|
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(
|
|
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
|
-
|
|
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(`
|
|
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
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
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 (
|
|
799
|
-
|
|
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
|
-
|
|
978
|
+
return out;
|
|
802
979
|
}
|
|
803
|
-
function
|
|
980
|
+
function parseBacktick(src, i) {
|
|
804
981
|
let j = i + 1;
|
|
805
982
|
while (j < src.length) {
|
|
806
|
-
|
|
983
|
+
const c = src[j];
|
|
984
|
+
if (c === "\\") {
|
|
807
985
|
j += 2;
|
|
808
986
|
continue;
|
|
809
987
|
}
|
|
810
|
-
if (
|
|
811
|
-
|
|
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
|
-
|
|
998
|
+
return { value: reverseBacktickBody(src.slice(i + 1, j - 1)), end: j };
|
|
818
999
|
}
|
|
819
|
-
function
|
|
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
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
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 === "
|
|
1015
|
+
if (c === "(" || c === "[" || c === "{") {
|
|
834
1016
|
depth++;
|
|
835
1017
|
j++;
|
|
836
1018
|
continue;
|
|
837
1019
|
}
|
|
838
|
-
if (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
|
-
|
|
1029
|
+
return { value: `{{${src.slice(i, j).trim()}}}`, end: j };
|
|
847
1030
|
}
|
|
848
|
-
function
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
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
|
|
873
|
-
const
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
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, "
|
|
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-
|
|
1683
|
+
//# sourceMappingURL=chunk-HNGBRPTL.js.map
|