rip-lang 3.13.108 → 3.13.109

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/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ All notable changes to Rip will be documented in this file.
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
8
8
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
+ ## [Unreleased]
11
+
12
+ ### Breaking — Merge Assign Operator
13
+
14
+ - **`*>` replaces `*` for merge assignment** — The merge-assign form is now `*>target = value` (including `*>@ = props`). The old `*target = value` form is removed.
15
+
10
16
  ## [3.13.16] - 2026-02-25
11
17
 
12
18
  ### Breaking — Merge `@rip-lang/api` into `@rip-lang/server`
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.108-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.109-blue.svg" alt="Version"></a>
13
13
  <a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
14
14
  <a href="#"><img src="https://img.shields.io/badge/tests-1%2C436%2F1%2C436-brightgreen.svg" alt="Tests"></a>
15
15
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
@@ -229,7 +229,7 @@ All use `globalThis` with `??=` — override any by redeclaring locally.
229
229
  | `not in` | `x not in arr` | Negated membership test |
230
230
  | `not of` | `k not of obj` | Negated key existence |
231
231
  | `.=` (method assign) | `x .= trim()` | `x = x.trim()` — compound method assignment |
232
- | `*` (merge assign) | `*obj = {a: 1}` | `Object.assign(obj, {a: 1})` |
232
+ | `*>` (merge assign) | `*>obj = {a: 1}` | `Object.assign(obj, {a: 1})` |
233
233
  | `or return` | `x = get() or return err` | Guard clause (Ruby-style) |
234
234
  | `?? throw` | `x = get() ?? throw err` | Nullish guard |
235
235
 
package/docs/RIP-LANG.md CHANGED
@@ -326,7 +326,7 @@ Multiple lines
326
326
  | `<` `<=` | Chained comparison | `1 < x < 10` | `(1 < x) && (x < 10)` |
327
327
  | `\|>` | Pipe | `x \|> fn` or `x \|> fn(y)` | `fn(x)` or `fn(x, y)` |
328
328
  | `.=` | Method assign | `x .= trim()` | `x = x.trim()` |
329
- | `*` | Merge assign | `*obj = {a: 1}` | `Object.assign(obj, {a: 1})` |
329
+ | `*>` | Merge assign | `*>obj = {a: 1}` | `Object.assign(obj, {a: 1})` |
330
330
  | `not in` | Not in | `x not in arr` | Negated membership test |
331
331
  | `not of` | Not of | `k not of obj` | Negated key existence |
332
332
  | `<=>` | Two-way bind | `value <=> name` | Bidirectional reactive binding (render blocks) |
@@ -535,37 +535,37 @@ str.=trim() # compact (no spaces)
535
535
  str .=trim() # mixed
536
536
  ```
537
537
 
538
- ## Merge Assignment (`*`)
538
+ ## Merge Assignment (`*>`)
539
539
 
540
540
  A Rip original. Merge properties into an existing object without repeating
541
541
  its name:
542
542
 
543
543
  ```coffee
544
- # Without * — repeat the object name or use verbose Object.assign
544
+ # Without *> — repeat the object name or use verbose Object.assign
545
545
  Object.assign config,
546
546
  host: "localhost"
547
547
  port: 3000
548
548
  debug: true
549
549
 
550
- # With * — clean and direct
551
- *config =
550
+ # With *> — clean and direct
551
+ *>config =
552
552
  host: "localhost"
553
553
  port: 3000
554
554
  debug: true
555
555
 
556
556
  # Single line
557
- *opts = {method: "POST", body: data}
557
+ *>opts = {method: "POST", body: data}
558
558
 
559
559
  # Dotted paths
560
- *el.style =
560
+ *>el.style =
561
561
  color: "red"
562
562
  fontSize: "14px"
563
563
 
564
564
  # Merge user overrides into defaults
565
- *defaults = userConfig
565
+ *>defaults = userConfig
566
566
  ```
567
567
 
568
- `*target = value` compiles to `Object.assign(target, value)`. The `*` reads
568
+ `*>target = value` compiles to `Object.assign(target, value)`. The `*>` reads
569
569
  as "spread these into" — the same concept as `...` spread but as an
570
570
  assignment. This is unique to Rip.
571
571
 
package/docs/dist/rip.js CHANGED
@@ -453,15 +453,29 @@
453
453
  }
454
454
  let emitBlock = (prefix, body, suffix) => {
455
455
  if (body.startsWith("{ ") && body.endsWith(" }")) {
456
- let props = body.slice(2, -2).split("; ").filter((p) => p.trim());
457
- if (props.length > 0) {
458
- lines.push(`${indent()}${prefix}{`);
459
- indentLevel++;
460
- for (let prop of props)
461
- lines.push(`${indent()}${prop};`);
462
- indentLevel--;
463
- lines.push(`${indent()}}${suffix}`);
464
- return;
456
+ let depth = 0, firstTopClose = -1;
457
+ for (let c = 0;c < body.length; c++) {
458
+ if (body[c] === "{")
459
+ depth++;
460
+ else if (body[c] === "}") {
461
+ depth--;
462
+ if (depth === 0) {
463
+ firstTopClose = c;
464
+ break;
465
+ }
466
+ }
467
+ }
468
+ if (firstTopClose === body.length - 1) {
469
+ let props = body.slice(2, -2).split("; ").filter((p) => p.trim());
470
+ if (props.length > 0) {
471
+ lines.push(`${indent()}${prefix}{`);
472
+ indentLevel++;
473
+ for (let prop of props)
474
+ lines.push(`${indent()}${prop};`);
475
+ indentLevel--;
476
+ lines.push(`${indent()}}${suffix}`);
477
+ return;
478
+ }
465
479
  }
466
480
  }
467
481
  lines.push(`${indent()}${prefix}${body}${suffix}`);
@@ -1465,7 +1479,7 @@
1465
1479
  var UNARY_MATH = new Set(["!", "~"]);
1466
1480
  var IDENTIFIER_RE = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+(?:!(?!\?)|[?](?![.?![(]))?)([^\n\S]*:(?![=:>]))?/;
1467
1481
  var NUMBER_RE = /^0b[01](?:_?[01])*n?|^0o[0-7](?:_?[0-7])*n?|^0x[\da-f](?:_?[\da-f])*n?|^\d+(?:_\d+)*n|^(?:\d+(?:_\d+)*)?\.?\d+(?:_\d+)*(?:e[+-]?\d+(?:_\d+)*)?/i;
1468
- var OPERATOR_RE = /^(?:<=>|::|[-=]>|~>|~=|:>|:=|=!|===|!==|!\?|\?\!|\?\?|=~|\|>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?\.?|\.{2,3})/;
1482
+ var OPERATOR_RE = /^(?:<=>|::|\*>|[-=]>|~>|~=|:>|:=|=!|===|!==|!\?|\?\!|\?\?|=~|\|>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?\.?|\.{2,3})/;
1469
1483
  var WHITESPACE_RE = /^[^\n\S]+/;
1470
1484
  var NEWLINE_RE = /^(?:\n[^\n\S]*)+/;
1471
1485
  var COMMENT_RE = /^(\s*)###([^#][\s\S]*?)(?:###([^\n\S]*)|###$)|^((?:\s*#(?!##[^#]).*)+)/;
@@ -2262,8 +2276,8 @@
2262
2276
  } else if (val === "=!") {
2263
2277
  tag = "READONLY_ASSIGN";
2264
2278
  this.inTypeAnnotation = false;
2265
- } else if (val === "*" && (!prev || prev[0] === "TERMINATOR" || prev[0] === "INDENT" || prev[0] === "OUTDENT") && (/^[a-zA-Z_$]/.test(this.chunk[1] || "") || this.chunk[1] === "@")) {
2266
- let rest = this.chunk.slice(1);
2279
+ } else if (val === "*>" && (!prev || prev[0] === "TERMINATOR" || prev[0] === "INDENT" || prev[0] === "OUTDENT") && (/^[a-zA-Z_$]/.test(this.chunk[2] || "") || this.chunk[2] === "@")) {
2280
+ let rest = this.chunk.slice(2);
2267
2281
  let mAt = /^@(\s*)=(?!=)/.exec(rest);
2268
2282
  if (mAt) {
2269
2283
  let space = mAt[1];
@@ -2273,7 +2287,7 @@
2273
2287
  t.spaced = true;
2274
2288
  this.emit("@", "@");
2275
2289
  this.emit(",", ",");
2276
- return 1 + 1 + space.length + 1;
2290
+ return 2 + 1 + space.length + 1;
2277
2291
  }
2278
2292
  let m = /^((?:(?!\s)[$\w\x7f-\uffff])+(?:\.[a-zA-Z_$][\w]*)*)(\s*)=(?!=)/.exec(rest);
2279
2293
  if (m) {
@@ -2299,7 +2313,7 @@
2299
2313
  this.emit(",", ",");
2300
2314
  let comma = this.prev();
2301
2315
  comma.mergeClose = true;
2302
- return 1 + target.length + space.length + 1;
2316
+ return 2 + target.length + space.length + 1;
2303
2317
  }
2304
2318
  } else if (val === "*" && prev?.[0] === "EXPORT")
2305
2319
  tag = "EXPORT_ALL";
@@ -4491,6 +4505,51 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
4491
4505
  }
4492
4506
  return props;
4493
4507
  };
4508
+ const extractIntrinsicProps = (args) => {
4509
+ const props = [];
4510
+ for (const arg of args) {
4511
+ let obj = null;
4512
+ if (this.is(arg, "object")) {
4513
+ obj = arg;
4514
+ } else if (Array.isArray(arg) && (arg[0] === "->" || arg[0] === "=>") && this.is(arg[2], "block")) {
4515
+ for (let k = 1;k < arg[2].length; k++) {
4516
+ if (this.is(arg[2][k], "object")) {
4517
+ obj = arg[2][k];
4518
+ break;
4519
+ }
4520
+ }
4521
+ }
4522
+ if (obj) {
4523
+ for (let j = 1;j < obj.length; j++) {
4524
+ const pair = obj[j];
4525
+ if (!Array.isArray(pair) || pair.length < 2)
4526
+ continue;
4527
+ const [key, value] = pair;
4528
+ const srcLine = pair.loc?.r ?? obj.loc?.r;
4529
+ if (Array.isArray(key) && key[0] === "." && key[1] === "this") {
4530
+ let memberName = typeof key[2] === "string" ? key[2] : key[2]?.valueOf?.();
4531
+ if (!memberName)
4532
+ continue;
4533
+ const eventKey = "@" + memberName.split(".")[0];
4534
+ const val = this.generateInComponent(value, "value");
4535
+ props.push({ code: `'${eventKey}': ${val}`, srcLine });
4536
+ } else if (typeof key === "string") {
4537
+ if (key === "key")
4538
+ continue;
4539
+ if (key.startsWith("__bind_") && key.endsWith("__")) {
4540
+ const propName = key.slice(7, -2);
4541
+ const val = this.generateInComponent(value, "value");
4542
+ props.push({ code: `${propName}: ${val}`, srcLine });
4543
+ } else {
4544
+ const val = this.generateInComponent(value, "value");
4545
+ props.push({ code: `${key}: ${val}`, srcLine });
4546
+ }
4547
+ }
4548
+ }
4549
+ }
4550
+ }
4551
+ return props;
4552
+ };
4494
4553
  const walkRender = (node) => {
4495
4554
  if (!Array.isArray(node))
4496
4555
  return;
@@ -4503,7 +4562,7 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
4503
4562
  const tagLine = node.loc?.r;
4504
4563
  constructions.push(` const ${varName}: ${propsType} = {};` + (tagLine != null ? ` // @rip-src:${tagLine}` : ""));
4505
4564
  } else if (props.length === 1) {
4506
- const srcLine = props[0].srcLine ?? node.loc?.r;
4565
+ const srcLine = node.loc?.r ?? props[0].srcLine;
4507
4566
  constructions.push(` const ${varName}: ${propsType} = {${props[0].code}};` + (srcLine != null ? ` // @rip-src:${srcLine}` : ""));
4508
4567
  } else {
4509
4568
  const tagLine = node.loc?.r;
@@ -4519,6 +4578,31 @@ Expecting ${expected.join(", ")}, got '${this.tokenNames[symbol] || symbol}'`;
4519
4578
  constructions.push(` };`);
4520
4579
  }
4521
4580
  }
4581
+ } else if (typeof head2 === "string" && head2 !== "object" && head2 !== "switch" && TEMPLATE_TAGS.has(head2.split(/[.#]/)[0])) {
4582
+ const tagName = head2.split(/[.#]/)[0];
4583
+ const iProps = extractIntrinsicProps(node.slice(1));
4584
+ const tagLine = node.loc?.r;
4585
+ const srcMarker = tagLine != null ? ` // @rip-src:${tagLine}` : "";
4586
+ if (iProps.length === 0) {
4587
+ constructions.push(` __ripEl('${tagName}');${srcMarker}`);
4588
+ } else if (iProps.length === 1) {
4589
+ const srcLine = iProps[0].srcLine ?? tagLine;
4590
+ const marker = srcLine != null ? ` // @rip-src:${srcLine}` : "";
4591
+ constructions.push(` __ripEl('${tagName}', {${iProps[0].code}});${marker}`);
4592
+ } else {
4593
+ const distinctLines = new Set(iProps.map((p) => p.srcLine).filter((l) => l != null));
4594
+ if (distinctLines.size <= 1) {
4595
+ const srcLine = iProps[0].srcLine ?? tagLine;
4596
+ const marker = srcLine != null ? ` // @rip-src:${srcLine}` : "";
4597
+ constructions.push(` __ripEl('${tagName}', {${iProps.map((p) => p.code).join(", ")}});${marker}`);
4598
+ } else {
4599
+ constructions.push(` __ripEl('${tagName}', {${srcMarker}`);
4600
+ for (const p of iProps) {
4601
+ constructions.push(` ${p.code},` + (p.srcLine != null ? ` // @rip-src:${p.srcLine}` : ""));
4602
+ }
4603
+ constructions.push(` });`);
4604
+ }
4605
+ }
4522
4606
  }
4523
4607
  for (let i = 1;i < node.length; i++)
4524
4608
  walkRender(node[i]);
@@ -9388,8 +9472,8 @@ globalThis.zip ??= (...a) => a[0].map((_, i) => a.map(b => b[i]));
9388
9472
  return new CodeGenerator({}).getComponentRuntime();
9389
9473
  }
9390
9474
  // src/browser.js
9391
- var VERSION = "3.13.108";
9392
- var BUILD_DATE = "2026-03-13@23:35:48GMT";
9475
+ var VERSION = "3.13.109";
9476
+ var BUILD_DATE = "2026-03-14@08:22:53GMT";
9393
9477
  if (typeof globalThis !== "undefined") {
9394
9478
  if (!globalThis.__rip)
9395
9479
  new Function(getReactiveRuntime())();
@@ -9690,6 +9774,8 @@ ${indented}`);
9690
9774
  var __batch;
9691
9775
  var __effect;
9692
9776
  var __state;
9777
+ var _ariaBindDialog;
9778
+ var _ariaBindPopover;
9693
9779
  var _ariaListNav;
9694
9780
  var _ariaLockScroll;
9695
9781
  var _ariaModalStack;
@@ -10863,6 +10949,66 @@ ${indented}`);
10863
10949
  return window.removeEventListener("scroll", onScroll, true);
10864
10950
  };
10865
10951
  };
10952
+ _ariaBindPopover = function(open, popover, setOpen, source = null) {
10953
+ let desired, el, get, onToggle, opts, shown, src;
10954
+ get = function(x) {
10955
+ return typeof x === "function" ? x() : x;
10956
+ };
10957
+ el = get(popover);
10958
+ if (!el)
10959
+ return;
10960
+ if (!Object.hasOwn(HTMLElement.prototype, "togglePopover"))
10961
+ return;
10962
+ onToggle = function(e) {
10963
+ return setOpen?.(e.newState === "open");
10964
+ };
10965
+ el.addEventListener("toggle", onToggle);
10966
+ shown = el.matches(":popover-open");
10967
+ desired = !!open;
10968
+ if (shown !== desired) {
10969
+ src = get(source);
10970
+ opts = src && desired ? { force: desired, source: src } : { force: desired };
10971
+ try {
10972
+ el.togglePopover(opts);
10973
+ } catch {}
10974
+ }
10975
+ return function() {
10976
+ return el.removeEventListener("toggle", onToggle);
10977
+ };
10978
+ };
10979
+ _ariaBindDialog = function(open, dialog, setOpen, dismissable = true) {
10980
+ let el, get, onCancel, onClose;
10981
+ get = function(x) {
10982
+ return typeof x === "function" ? x() : x;
10983
+ };
10984
+ el = get(dialog);
10985
+ if (!el?.showModal)
10986
+ return;
10987
+ onCancel = function(e) {
10988
+ if (!dismissable) {
10989
+ e.preventDefault();
10990
+ return;
10991
+ }
10992
+ return setOpen?.(false);
10993
+ };
10994
+ onClose = function() {
10995
+ return setOpen?.(false);
10996
+ };
10997
+ el.addEventListener("cancel", onCancel);
10998
+ el.addEventListener("close", onClose);
10999
+ if (open && !el.open) {
11000
+ try {
11001
+ el.showModal();
11002
+ } catch {}
11003
+ }
11004
+ if (!open && el.open) {
11005
+ el.close();
11006
+ }
11007
+ return function() {
11008
+ el.removeEventListener("cancel", onCancel);
11009
+ return el.removeEventListener("close", onClose);
11010
+ };
11011
+ };
10866
11012
  _ariaRovingNav = function(e, h, orientation = "vertical") {
10867
11013
  let horz, vert;
10868
11014
  if (e.isComposing)
@@ -10973,7 +11119,7 @@ ${indented}`);
10973
11119
  return window.scrollTo(0, scrollY);
10974
11120
  }
10975
11121
  };
10976
- globalThis.__aria ??= { listNav: _ariaListNav, rovingNav: _ariaRovingNav, popupDismiss: _ariaPopupDismiss, positionBelow: _ariaPositionBelow, trapFocus: _ariaTrapFocus, wireAria: _ariaWireAria, lockScroll: _ariaLockScroll, unlockScroll: _ariaUnlockScroll };
11122
+ globalThis.__aria ??= { listNav: _ariaListNav, rovingNav: _ariaRovingNav, popupDismiss: _ariaPopupDismiss, bindPopover: _ariaBindPopover, bindDialog: _ariaBindDialog, positionBelow: _ariaPositionBelow, trapFocus: _ariaTrapFocus, wireAria: _ariaWireAria, lockScroll: _ariaLockScroll, unlockScroll: _ariaUnlockScroll };
10977
11123
  globalThis.ARIA ??= globalThis.__aria;
10978
11124
 
10979
11125
  // docs/dist/_entry.js