@barefootjs/mojolicious 0.5.0 → 0.5.2

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.
@@ -501,7 +501,7 @@ function buildPerlProps(
501
501
  * Evaluate a signal initializer expression using provided props.
502
502
  * Handles patterns like: props.initial ?? 0, props.value, literal values.
503
503
  */
504
- function evaluateSignalInit(
504
+ export function evaluateSignalInit(
505
505
  expr: string,
506
506
  props?: Record<string, unknown>,
507
507
  ): unknown {
@@ -534,6 +534,29 @@ function parseLiteral(expr: string): unknown {
534
534
  if (expr === 'true') return true
535
535
  if (expr === 'false') return false
536
536
  if (expr === '[]') return []
537
+
538
+ // Non-empty array literal (`[{ id: 'a' }, { id: 'b' }]`, `['x', 'y']`).
539
+ // Each element is parsed recursively; if any element can't be parsed
540
+ // (identifier, call, member access, …) the whole array bails to null so
541
+ // the harness falls back to its `undef` behaviour. Mirrors the object-
542
+ // literal branch below. Needed so signal initial values that are inline
543
+ // object/scalar arrays seed the Mojo SSR stash (e.g. the whole-item loop
544
+ // conditional fixture, whose `items` is `[{ id: 'a' }, …]`).
545
+ {
546
+ const t = expr.trim()
547
+ if (t.startsWith('[') && t.endsWith(']')) {
548
+ const inner = t.slice(1, -1).trim()
549
+ if (!inner) return []
550
+ const out: unknown[] = []
551
+ for (const seg of splitTopLevelCommas(inner)) {
552
+ if (!seg.trim()) continue
553
+ const parsed = parseLiteral(seg.trim())
554
+ if (parsed === null && seg.trim() !== 'null') return null
555
+ out.push(parsed)
556
+ }
557
+ return out
558
+ }
559
+ }
537
560
  // String literal — require matching opener/closer (the previous
538
561
  // regex `^['"]…['"]$` accepted mixed quotes like `'foo"`) and
539
562
  // unescape JS-style escape sequences so `'a\\'b'` round-trips as
@@ -559,42 +582,7 @@ function parseLiteral(expr: string): unknown {
559
582
  const inner = trimmed.slice(1, -1).trim()
560
583
  if (!inner) return {}
561
584
  const obj: Record<string, unknown> = {}
562
- // Split on commas at depth 0; values may contain `:` so use a
563
- // simple state machine instead of a regex.
564
- const pairs: string[] = []
565
- let depth = 0
566
- let start = 0
567
- let quote: string | null = null
568
- for (let i = 0; i < inner.length; i++) {
569
- const c = inner[i]
570
- if (quote) {
571
- // Quote-termination check: count consecutive backslashes
572
- // immediately before the current position. An EVEN count
573
- // means each `\\` pair represents a literal backslash and
574
- // the quote is unescaped, closing the string. An ODD
575
- // count means the trailing `\` escapes the quote, so the
576
- // string keeps going. The naive `inner[i-1] !== '\\'`
577
- // check mis-classifies even runs (`\\"`) as escaped
578
- // (#1413 review).
579
- if (c === quote) {
580
- let backslashes = 0
581
- for (let j = i - 1; j >= 0 && inner[j] === '\\'; j--) backslashes++
582
- if (backslashes % 2 === 0) quote = null
583
- }
584
- continue
585
- }
586
- if (c === '"' || c === "'") {
587
- quote = c
588
- continue
589
- }
590
- if (c === '{' || c === '[') depth++
591
- else if (c === '}' || c === ']') depth--
592
- else if (c === ',' && depth === 0) {
593
- pairs.push(inner.slice(start, i))
594
- start = i + 1
595
- }
596
- }
597
- pairs.push(inner.slice(start))
585
+ const pairs = splitTopLevelCommas(inner)
598
586
  for (const pair of pairs) {
599
587
  // Skip empty segments — typically a trailing comma's tail
600
588
  // (#1413 review).
@@ -617,6 +605,43 @@ function parseLiteral(expr: string): unknown {
617
605
  return null
618
606
  }
619
607
 
608
+ /**
609
+ * Split a comma-separated literal body (object-pair list or array element
610
+ * list) on top-level commas only — commas nested inside braces, brackets, or
611
+ * string literals don't split. Backslash-escaped quotes inside strings are
612
+ * honoured (an odd run of backslashes before a quote keeps the string open).
613
+ * Shared by the object- and array-literal branches of {@link parseLiteral}.
614
+ */
615
+ function splitTopLevelCommas(inner: string): string[] {
616
+ const segments: string[] = []
617
+ let depth = 0
618
+ let start = 0
619
+ let quote: string | null = null
620
+ for (let i = 0; i < inner.length; i++) {
621
+ const c = inner[i]
622
+ if (quote) {
623
+ if (c === quote) {
624
+ let backslashes = 0
625
+ for (let j = i - 1; j >= 0 && inner[j] === '\\'; j--) backslashes++
626
+ if (backslashes % 2 === 0) quote = null
627
+ }
628
+ continue
629
+ }
630
+ if (c === '"' || c === "'") {
631
+ quote = c
632
+ continue
633
+ }
634
+ if (c === '{' || c === '[') depth++
635
+ else if (c === '}' || c === ']') depth--
636
+ else if (c === ',' && depth === 0) {
637
+ segments.push(inner.slice(start, i))
638
+ start = i + 1
639
+ }
640
+ }
641
+ segments.push(inner.slice(start))
642
+ return segments
643
+ }
644
+
620
645
  /**
621
646
  * Unescape a JS string-literal body (the content between the
622
647
  * matching opening and closing quotes, not the quotes themselves).