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 +6 -0
- package/README.md +2 -2
- package/docs/RIP-LANG.md +9 -9
- package/docs/dist/rip.js +164 -18
- package/docs/dist/rip.min.js +163 -163
- package/docs/dist/rip.min.js.br +0 -0
- package/package.json +4 -1
- package/src/components.js +65 -3
- package/src/lexer.js +9 -9
- package/src/typecheck.js +54 -13
- package/src/types.js +17 -9
- package/src/ui.rip +59 -0
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.
|
|
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
|
-
|
|
|
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
|
-
|
|
|
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
|
|
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
|
|
551
|
-
|
|
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
|
-
|
|
557
|
+
*>opts = {method: "POST", body: data}
|
|
558
558
|
|
|
559
559
|
# Dotted paths
|
|
560
|
-
|
|
560
|
+
*>el.style =
|
|
561
561
|
color: "red"
|
|
562
562
|
fontSize: "14px"
|
|
563
563
|
|
|
564
564
|
# Merge user overrides into defaults
|
|
565
|
-
|
|
565
|
+
*>defaults = userConfig
|
|
566
566
|
```
|
|
567
567
|
|
|
568
|
-
|
|
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
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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 = /^(
|
|
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 === "
|
|
2266
|
-
let rest = this.chunk.slice(
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
9392
|
-
var BUILD_DATE = "2026-03-
|
|
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
|