rip-lang 3.7.0 → 3.7.4

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,31 @@ 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
+ ## [3.7.4] - 2026-02-11
11
+
12
+ ### Compiler — Nested Function Scope Chain
13
+
14
+ - **Scope stack for variable hoisting** — Nested functions no longer re-declare variables from enclosing function scopes. Previously, the compiler only checked program-level variables; now it tracks a full scope chain. Fixes incorrect `let` shadowing in deeply nested function patterns (e.g., `mountRoute` inside `createRenderer` in `ui.rip`).
15
+
16
+ ### Rip UI — Demo App Working
17
+
18
+ - **Router `navigating` getter/setter** — The `_navigating` signal is now accessible through `router.navigating` (read/write), fixing a cross-scope reference where `createRenderer` accessed a variable local to `createRouter`.
19
+ - **Component props passthrough** — `__Component` base class now assigns all props to `this` via `Object.assign(this, props)` before `_init`, so components can access `@router`, `@app`, `@params` etc.
20
+
21
+ ### Language — `*@` Merge-This
22
+
23
+ - **`*@ = props`** — New merge-assign variant for `this`. Compiles to `Object.assign(this, props)`. Natural extension of `*obj = expr` — no `??=` guard needed since `this` is never null.
24
+
25
+ ## [3.7.3] - 2026-02-11
26
+
27
+ ### Fixes & Polish
28
+
29
+ - **Interface `::` fix** — `::` type annotations in interface bodies no longer produce double colons in `.d.ts` output.
30
+ - **`.=` dot chain support** — `obj.name .= toUpperCase()` now works (walks back full property chain).
31
+ - **`*` merge auto-init** — `*foo = {...}` auto-creates `foo` if undefined via `??= {}`.
32
+ - **`loop n` uses `it`** — Loop counter is accessible as `it` inside the body.
33
+ - **Syntax highlighting** — `|>` pipe operator added to VS Code, Vim, Playground, and rip-print.
34
+
10
35
  ## [3.7.0] - 2026-02-11
11
36
 
12
37
  ### Pipe Operator (`|>`)
package/README.md CHANGED
@@ -9,9 +9,9 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.7.0-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.7.4-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
- <a href="#"><img src="https://img.shields.io/badge/tests-1219%2F1219-brightgreen.svg" alt="Tests"></a>
14
+ <a href="#"><img src="https://img.shields.io/badge/tests-1225%2F1225-brightgreen.svg" alt="Tests"></a>
15
15
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
16
16
  </p>
17
17
 
@@ -267,7 +267,7 @@ Counter = component
267
267
 
268
268
  Two keywords — `component` and `render` — are all the language adds. Everything else (`:=` state, `~=` computed, methods, lifecycle) is standard Rip.
269
269
 
270
- See [@rip-lang/ui](packages/ui/) for the full framework: file-based router, reactive stash, parts system, and component renderer.
270
+ See [@rip-lang/ui](packages/ui/) for the full framework: file-based router, reactive stash, component store, and renderer.
271
271
 
272
272
  ---
273
273
 
@@ -344,7 +344,7 @@ Rip includes optional packages for full-stack development:
344
344
 
345
345
  | Package | Version | Purpose |
346
346
  |---------|---------|---------|
347
- | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.7.0 | Core language compiler |
347
+ | [rip-lang](https://www.npmjs.com/package/rip-lang) | 3.7.4 | Core language compiler |
348
348
  | [@rip-lang/api](packages/api/) | 1.1.4 | HTTP framework (Sinatra-style routing, 37 validators) |
349
349
  | [@rip-lang/server](packages/server/) | 1.1.3 | Multi-worker app server (hot reload, HTTPS, mDNS) |
350
350
  | [@rip-lang/db](packages/db/) | 1.1.2 | DuckDB server with official UI (pure Bun FFI) |
@@ -390,7 +390,7 @@ rip file.rip # Run
390
390
  rip -c file.rip # Compile
391
391
  rip -t file.rip # Tokens
392
392
  rip -s file.rip # S-expressions
393
- bun run test # 1219 tests
393
+ bun run test # 1225 tests
394
394
  bun run parser # Rebuild parser
395
395
  bun run browser # Build browser bundle
396
396
  ```
package/docs/RIP-LANG.md CHANGED
@@ -412,7 +412,14 @@ users .= map -> it.name
412
412
  users .= sort()
413
413
  ```
414
414
 
415
- Works with any method — built-in or custom, with or without arguments.
415
+ Works with any method — built-in or custom, with or without arguments. Spacing
416
+ is flexible — all of these are equivalent:
417
+
418
+ ```coffee
419
+ str .= trim() # canonical (spaced)
420
+ str.=trim() # compact (no spaces)
421
+ str .=trim() # mixed
422
+ ```
416
423
 
417
424
  ## Merge Assignment (`*`)
418
425
 
@@ -439,7 +439,7 @@ function collectStructuralType(tokens, indentIdx) {
439
439
  optional = true;
440
440
  j++;
441
441
  }
442
- if (tokens[j]?.[1] === ":")
442
+ if (tokens[j]?.[1] === ":" || tokens[j]?.[0] === "TYPE_ANNOTATION")
443
443
  j++;
444
444
  let propTypeTokens = [];
445
445
  let typeDepth = 0;
@@ -1993,13 +1993,18 @@ class Lexer {
1993
1993
  if (CODE_RE.test(val))
1994
1994
  this.tagParameters();
1995
1995
  if (val === "=" && prev && prev[1] === "." && !prev.spaced) {
1996
- let target = this.tokens[this.tokens.length - 2];
1997
- if (target && (target[0] === "IDENTIFIER" || target[0] === "PROPERTY" || target[0] === ")" || target[0] === "]")) {
1996
+ let tokens = this.tokens;
1997
+ let j = tokens.length - 2;
1998
+ while (j >= 1 && tokens[j][0] === "PROPERTY" && tokens[j - 1]?.[1] === ".")
1999
+ j -= 2;
2000
+ if (j >= 0 && (tokens[j][0] === "IDENTIFIER" || tokens[j][0] === ")" || tokens[j][0] === "]")) {
2001
+ let chainTokens = tokens.slice(j, tokens.length - 1);
1998
2002
  prev[0] = "=";
1999
2003
  prev[1] = "=";
2000
- let targetClone = tok(target[0], target[1], { pre: 0, row: target[2], col: target[3], len: target[4] });
2001
- let dot = tok(".", ".", { pre: 0, row: this.row, col: this.col, len: 1 });
2002
- this.tokens.push(targetClone, dot);
2004
+ for (let t of chainTokens) {
2005
+ this.tokens.push(tok(t[0], t[1], { pre: 0, row: t[2], col: t[3], len: t[4] }));
2006
+ }
2007
+ this.tokens.push(tok(".", ".", { pre: 0, row: this.row, col: this.col, len: 1 }));
2003
2008
  return val.length;
2004
2009
  }
2005
2010
  }
@@ -2044,21 +2049,40 @@ class Lexer {
2044
2049
  tag = "REACT_ASSIGN";
2045
2050
  else if (val === "=!")
2046
2051
  tag = "READONLY_ASSIGN";
2047
- else if (val === "*" && (!prev || prev[0] === "TERMINATOR" || prev[0] === "INDENT" || prev[0] === "OUTDENT") && /^[a-zA-Z_$]/.test(this.chunk[1] || "")) {
2052
+ else if (val === "*" && (!prev || prev[0] === "TERMINATOR" || prev[0] === "INDENT" || prev[0] === "OUTDENT") && (/^[a-zA-Z_$]/.test(this.chunk[1] || "") || this.chunk[1] === "@")) {
2048
2053
  let rest = this.chunk.slice(1);
2054
+ let mAt = /^@(\s*)=(?!=)/.exec(rest);
2055
+ if (mAt) {
2056
+ let space = mAt[1];
2057
+ this.emit("IDENTIFIER", "Object");
2058
+ this.emit(".", ".");
2059
+ let t = this.emit("PROPERTY", "assign");
2060
+ t.spaced = true;
2061
+ this.emit("@", "@");
2062
+ this.emit(",", ",");
2063
+ return 1 + 1 + space.length + 1;
2064
+ }
2049
2065
  let m = /^((?:(?!\s)[$\w\x7f-\uffff])+(?:\.[a-zA-Z_$][\w]*)*)(\s*)=(?!=)/.exec(rest);
2050
2066
  if (m) {
2051
2067
  let target = m[1], space = m[2];
2068
+ let parts = target.split(".");
2069
+ let emitTarget = () => {
2070
+ this.emit("IDENTIFIER", parts[0]);
2071
+ for (let i = 1;i < parts.length; i++) {
2072
+ this.emit(".", ".");
2073
+ this.emit("PROPERTY", parts[i]);
2074
+ }
2075
+ };
2076
+ emitTarget();
2077
+ this.emit("=", "=");
2052
2078
  this.emit("IDENTIFIER", "Object");
2053
2079
  this.emit(".", ".");
2054
2080
  this.emit("PROPERTY", "assign");
2055
2081
  this.emit("CALL_START", "(");
2056
- let parts = target.split(".");
2057
- this.emit("IDENTIFIER", parts[0]);
2058
- for (let i = 1;i < parts.length; i++) {
2059
- this.emit(".", ".");
2060
- this.emit("PROPERTY", parts[i]);
2061
- }
2082
+ emitTarget();
2083
+ this.emit("COMPOUND_ASSIGN", "??=");
2084
+ this.emit("{", "{");
2085
+ this.emit("}", "}");
2062
2086
  this.emit(",", ",");
2063
2087
  let comma = this.prev();
2064
2088
  comma.mergeClose = true;
@@ -2353,7 +2377,7 @@ class Lexer {
2353
2377
  let prevToken = i > 0 ? tokens[i - 1] : null;
2354
2378
  let prevTag = prevToken ? prevToken[0] : null;
2355
2379
  let atLineStart = prevTag === "INDENT" || prevTag === "TERMINATOR";
2356
- let cxToken = gen("PROPERTY", "__cx__", token);
2380
+ let cxToken = gen("PROPERTY", "__clsx", token);
2357
2381
  nextToken[0] = "CALL_START";
2358
2382
  let depth = 1;
2359
2383
  for (let j = i + 2;j < tokens.length && depth > 0; j++) {
@@ -2403,6 +2427,10 @@ class Lexer {
2403
2427
  return 3;
2404
2428
  }
2405
2429
  }
2430
+ if (tag === "IDENTIFIER" && isComponent(token[1]) && nextToken && (nextToken[0] === "OUTDENT" || nextToken[0] === "TERMINATOR")) {
2431
+ tokens.splice(i + 1, 0, gen("CALL_START", "(", token), gen("CALL_END", ")", token));
2432
+ return 3;
2433
+ }
2406
2434
  return 1;
2407
2435
  });
2408
2436
  }
@@ -3566,6 +3594,9 @@ function installComponentSupport(CodeGenerator) {
3566
3594
  if (typeof sexpr === "string" && this.reactiveMembers && this.reactiveMembers.has(sexpr)) {
3567
3595
  return [".", [".", "this", sexpr], "value"];
3568
3596
  }
3597
+ if (typeof sexpr === "string" && this.componentMembers && this.componentMembers.has(sexpr)) {
3598
+ return [".", "this", sexpr];
3599
+ }
3569
3600
  return sexpr;
3570
3601
  }
3571
3602
  if (sexpr[0] === "." && sexpr[1] === "this" && typeof sexpr[2] === "string") {
@@ -3660,17 +3691,15 @@ function installComponentSupport(CodeGenerator) {
3660
3691
  this.reactiveMembers = reactiveMembers;
3661
3692
  const lines = [];
3662
3693
  let blockFactoriesCode = "";
3663
- lines.push("class {");
3664
- lines.push(" constructor(props = {}) {");
3665
- lines.push(" const __prevComponent = __pushComponent(this);");
3666
- lines.push("");
3694
+ lines.push("class extends __Component {");
3695
+ lines.push(" _init(props) {");
3667
3696
  for (const { name, value } of readonlyVars) {
3668
3697
  const val = this.generateInComponent(value, "value");
3669
3698
  lines.push(` this.${name} = props.${name} ?? ${val};`);
3670
3699
  }
3671
3700
  for (const { name, value } of stateVars) {
3672
3701
  const val = this.generateInComponent(value, "value");
3673
- lines.push(` this.${name} = isSignal(props.${name}) ? props.${name} : __state(props.${name} ?? ${val});`);
3702
+ lines.push(` this.${name} = __state(props.${name} ?? ${val});`);
3674
3703
  }
3675
3704
  for (const { name, expr } of derivedVars) {
3676
3705
  const val = this.generateInComponent(expr, "value");
@@ -3681,8 +3710,6 @@ function installComponentSupport(CodeGenerator) {
3681
3710
  const effectCode = this.generateInComponent(effectBody, "value");
3682
3711
  lines.push(` __effect(${effectCode});`);
3683
3712
  }
3684
- lines.push("");
3685
- lines.push(" __popComponent(__prevComponent);");
3686
3713
  lines.push(" }");
3687
3714
  for (const { name, func } of methods) {
3688
3715
  if (Array.isArray(func) && (func[0] === "->" || func[0] === "=>")) {
@@ -3723,21 +3750,6 @@ function installComponentSupport(CodeGenerator) {
3723
3750
  lines.push(" }");
3724
3751
  }
3725
3752
  }
3726
- lines.push(" mount(target) {");
3727
- lines.push(' if (typeof target === "string") target = document.querySelector(target);');
3728
- lines.push(" this._target = target;");
3729
- lines.push(" this._root = this._create();");
3730
- lines.push(" target.appendChild(this._root);");
3731
- lines.push(" if (this._setup) this._setup();");
3732
- lines.push(" if (this.mounted) this.mounted();");
3733
- lines.push(" return this;");
3734
- lines.push(" }");
3735
- lines.push(" unmount() {");
3736
- lines.push(" if (this.unmounted) this.unmounted();");
3737
- lines.push(" if (this._root && this._root.parentNode) {");
3738
- lines.push(" this._root.parentNode.removeChild(this._root);");
3739
- lines.push(" }");
3740
- lines.push(" }");
3741
3753
  lines.push("}");
3742
3754
  this.componentMembers = prevComponentMembers;
3743
3755
  this.reactiveMembers = prevReactiveMembers;
@@ -3754,6 +3766,9 @@ ${blockFactoriesCode}return ${lines.join(`
3754
3766
  if (typeof sexpr === "string" && this.reactiveMembers && this.reactiveMembers.has(sexpr)) {
3755
3767
  return `this.${sexpr}.value`;
3756
3768
  }
3769
+ if (typeof sexpr === "string" && this.componentMembers && this.componentMembers.has(sexpr)) {
3770
+ return `this.${sexpr}`;
3771
+ }
3757
3772
  if (Array.isArray(sexpr) && this.reactiveMembers) {
3758
3773
  const transformed = this.transformComponentMembers(sexpr);
3759
3774
  return this.generate(transformed, context);
@@ -3840,11 +3855,9 @@ ${blockFactoriesCode}return ${lines.join(`
3840
3855
  this._setupLines.push(`__effect(() => { ${textVar3}.data = this.${prop}.value; });`);
3841
3856
  return textVar3;
3842
3857
  }
3843
- if (this.componentMembers && this.componentMembers.has(prop)) {
3844
- const slotVar = this.newElementVar("slot");
3845
- this._createLines.push(`${slotVar} = this.${prop} instanceof Node ? this.${prop} : (this.${prop} != null ? document.createTextNode(String(this.${prop})) : document.createComment(''));`);
3846
- return slotVar;
3847
- }
3858
+ const slotVar = this.newElementVar("slot");
3859
+ this._createLines.push(`${slotVar} = this.${prop} instanceof Node ? this.${prop} : (this.${prop} != null ? document.createTextNode(String(this.${prop})) : document.createComment(''));`);
3860
+ return slotVar;
3848
3861
  }
3849
3862
  const { tag, classes } = this.collectTemplateClasses(sexpr);
3850
3863
  if (tag && this.isHtmlTag(tag)) {
@@ -3856,14 +3869,14 @@ ${blockFactoriesCode}return ${lines.join(`
3856
3869
  return textVar2;
3857
3870
  }
3858
3871
  if (Array.isArray(head)) {
3859
- if (Array.isArray(head[0]) && head[0][0] === "." && (head[0][2] === "__cx__" || head[0][2] instanceof String && head[0][2].valueOf() === "__cx__")) {
3872
+ if (Array.isArray(head[0]) && head[0][0] === "." && (head[0][2] === "__clsx" || head[0][2] instanceof String && head[0][2].valueOf() === "__clsx")) {
3860
3873
  const tag2 = typeof head[0][1] === "string" ? head[0][1] : head[0][1].valueOf();
3861
3874
  const classExprs = head.slice(1);
3862
3875
  return this.generateDynamicTag(tag2, classExprs, rest);
3863
3876
  }
3864
3877
  const { tag, classes } = this.collectTemplateClasses(head);
3865
3878
  if (tag && this.isHtmlTag(tag)) {
3866
- if (classes.length === 1 && classes[0] === "__cx__") {
3879
+ if (classes.length === 1 && classes[0] === "__clsx") {
3867
3880
  return this.generateDynamicTag(tag, rest, []);
3868
3881
  }
3869
3882
  return this.generateTag(tag, classes, rest);
@@ -3947,9 +3960,9 @@ ${blockFactoriesCode}return ${lines.join(`
3947
3960
  const classArgs = classExprs.map((e) => this.generateInComponent(e, "value")).join(", ");
3948
3961
  const hasReactive = classExprs.some((e) => this.hasReactiveDeps(e));
3949
3962
  if (hasReactive) {
3950
- this._setupLines.push(`__effect(() => { ${elVar}.className = __cx__(${classArgs}); });`);
3963
+ this._setupLines.push(`__effect(() => { ${elVar}.className = __clsx(${classArgs}); });`);
3951
3964
  } else {
3952
- this._createLines.push(`${elVar}.className = __cx__(${classArgs});`);
3965
+ this._createLines.push(`${elVar}.className = __clsx(${classArgs});`);
3953
3966
  }
3954
3967
  }
3955
3968
  for (const arg of children) {
@@ -3993,8 +4006,12 @@ ${blockFactoriesCode}return ${lines.join(`
3993
4006
  let [key, value] = objExpr[i];
3994
4007
  if (this.is(key, ".") && key[1] === "this") {
3995
4008
  const eventName = key[2];
3996
- const handlerCode = this.generateInComponent(value, "value");
3997
- this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => (${handlerCode})(e));`);
4009
+ if (typeof value === "string" && this.componentMembers?.has(value)) {
4010
+ this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => this.${value}(e));`);
4011
+ } else {
4012
+ const handlerCode = this.generateInComponent(value, "value");
4013
+ this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => (${handlerCode})(e));`);
4014
+ }
3998
4015
  continue;
3999
4016
  }
4000
4017
  if (typeof key === "string") {
@@ -4350,10 +4367,6 @@ ${blockFactoriesCode}return ${lines.join(`
4350
4367
  // Rip Component Runtime
4351
4368
  // ============================================================================
4352
4369
 
4353
- function isSignal(v) {
4354
- return v != null && typeof v === 'object' && typeof v.read === 'function';
4355
- }
4356
-
4357
4370
  let __currentComponent = null;
4358
4371
 
4359
4372
  function __pushComponent(component) {
@@ -4391,13 +4404,38 @@ function hasContext(key) {
4391
4404
  return false;
4392
4405
  }
4393
4406
 
4394
- function __cx__(...args) {
4407
+ function __clsx(...args) {
4395
4408
  return args.filter(Boolean).join(' ');
4396
4409
  }
4397
4410
 
4411
+ class __Component {
4412
+ constructor(props = {}) {
4413
+ Object.assign(this, props);
4414
+ const prev = __pushComponent(this);
4415
+ this._init(props);
4416
+ __popComponent(prev);
4417
+ }
4418
+ _init() {}
4419
+ mount(target) {
4420
+ if (typeof target === "string") target = document.querySelector(target);
4421
+ this._target = target;
4422
+ this._root = this._create();
4423
+ target.appendChild(this._root);
4424
+ if (this._setup) this._setup();
4425
+ if (this.mounted) this.mounted();
4426
+ return this;
4427
+ }
4428
+ unmount() {
4429
+ if (this.unmounted) this.unmounted();
4430
+ if (this._root && this._root.parentNode) {
4431
+ this._root.parentNode.removeChild(this._root);
4432
+ }
4433
+ }
4434
+ }
4435
+
4398
4436
  // Register on globalThis for runtime deduplication
4399
4437
  if (typeof globalThis !== 'undefined') {
4400
- globalThis.__ripComponent = { isSignal, __pushComponent, __popComponent, setContext, getContext, hasContext, __cx__ };
4438
+ globalThis.__ripComponent = { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component };
4401
4439
  }
4402
4440
 
4403
4441
  `;
@@ -4784,6 +4822,7 @@ class CodeGenerator {
4784
4822
  this.programVars = new Set;
4785
4823
  this.functionVars = new Map;
4786
4824
  this.helpers = new Set;
4825
+ this.scopeStack = [];
4787
4826
  this.collectProgramVariables(sexpr);
4788
4827
  let code = this.generate(sexpr);
4789
4828
  if (this.sourceMap)
@@ -5213,7 +5252,7 @@ class CodeGenerator {
5213
5252
  }
5214
5253
  if (this.usesTemplates && !skip) {
5215
5254
  if (typeof globalThis !== "undefined" && globalThis.__ripComponent) {
5216
- code += `const { isSignal, __pushComponent, __popComponent, setContext, getContext, hasContext, __cx__ } = globalThis.__ripComponent;
5255
+ code += `const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component } = globalThis.__ripComponent;
5217
5256
  `;
5218
5257
  } else {
5219
5258
  code += this.getComponentRuntime();
@@ -5638,7 +5677,7 @@ ${this.indent()}}`;
5638
5677
  generateLoopN(head, rest) {
5639
5678
  let [count, body] = rest;
5640
5679
  let n = this.generate(count, "value");
5641
- return `for (let _i = 0; _i < ${n}; _i++) ${this.generateLoopBody(body)}`;
5680
+ return `for (let it = 0; it < ${n}; it++) ${this.generateLoopBody(body)}`;
5642
5681
  }
5643
5682
  generateAwait(head, rest) {
5644
5683
  return `await ${this.generate(rest[0], "value")}`;
@@ -6686,9 +6725,10 @@ export default ${expr[1]}`;
6686
6725
  if (Array.isArray(params))
6687
6726
  params.forEach(extractPN);
6688
6727
  let bodyVars = this.collectFunctionVariables(body);
6689
- let newVars = new Set([...bodyVars].filter((v) => !this.programVars.has(v) && !this.reactiveVars?.has(v) && !paramNames.has(v)));
6728
+ let newVars = new Set([...bodyVars].filter((v) => !this.programVars.has(v) && !this.reactiveVars?.has(v) && !paramNames.has(v) && !this.scopeStack.some((s) => s.has(v))));
6690
6729
  let noRetStmts = ["return", "throw", "break", "continue"];
6691
6730
  let loopStmts = ["for-in", "for-of", "for-as", "while", "until", "loop"];
6731
+ this.scopeStack.push(new Set([...newVars, ...paramNames]));
6692
6732
  if (this.is(body, "block")) {
6693
6733
  let statements = this.unwrapBlock(body);
6694
6734
  if (hasExpansionParams && this.expansionAfterParams?.length > 0) {
@@ -6788,17 +6828,22 @@ export default ${expr[1]}`;
6788
6828
  }
6789
6829
  this.indentLevel--;
6790
6830
  code += this.indent() + "}";
6831
+ this.scopeStack.pop();
6791
6832
  this.sideEffectOnly = prevSEO;
6792
6833
  return code;
6793
6834
  }
6794
6835
  this.sideEffectOnly = prevSEO;
6836
+ let result;
6795
6837
  if (isConstructor || this.hasExplicitControlFlow(body))
6796
- return `{ ${this.generate(body, "statement")}; }`;
6797
- if (Array.isArray(body) && (noRetStmts.includes(body[0]) || loopStmts.includes(body[0])))
6798
- return `{ ${this.generate(body, "statement")}; }`;
6799
- if (sideEffectOnly)
6800
- return `{ ${this.generate(body, "statement")}; return; }`;
6801
- return `{ return ${this.generate(body, "value")}; }`;
6838
+ result = `{ ${this.generate(body, "statement")}; }`;
6839
+ else if (Array.isArray(body) && (noRetStmts.includes(body[0]) || loopStmts.includes(body[0])))
6840
+ result = `{ ${this.generate(body, "statement")}; }`;
6841
+ else if (sideEffectOnly)
6842
+ result = `{ ${this.generate(body, "statement")}; return; }`;
6843
+ else
6844
+ result = `{ return ${this.generate(body, "value")}; }`;
6845
+ this.scopeStack.pop();
6846
+ return result;
6802
6847
  }
6803
6848
  generateFunctionBody(body, params = [], sideEffectOnly = false) {
6804
6849
  return this.generateBodyWithReturns(body, params, { sideEffectOnly, hasExpansionParams: this.expansionAfterParams?.length > 0 });
@@ -7734,6 +7779,7 @@ const __primitiveCoercion = {
7734
7779
  };
7735
7780
 
7736
7781
  function __state(initialValue) {
7782
+ if (initialValue != null && typeof initialValue === 'object' && typeof initialValue.read === 'function') return initialValue;
7737
7783
  let value = initialValue;
7738
7784
  const subscribers = new Set();
7739
7785
  let notifying = false;
@@ -8028,8 +8074,8 @@ function getComponentRuntime() {
8028
8074
  return new CodeGenerator({}).getComponentRuntime();
8029
8075
  }
8030
8076
  // src/browser.js
8031
- var VERSION = "3.7.0";
8032
- var BUILD_DATE = "2026-02-11@09:35:09GMT";
8077
+ var VERSION = "3.7.4";
8078
+ var BUILD_DATE = "2026-02-11@13:07:41GMT";
8033
8079
  if (typeof globalThis !== "undefined" && !globalThis.__rip) {
8034
8080
  new Function(getReactiveRuntime())();
8035
8081
  }
@@ -8045,24 +8091,23 @@ async function processRipScripts() {
8045
8091
  continue;
8046
8092
  try {
8047
8093
  const ripCode = dedent(script.textContent);
8048
- const jsCode = compileToJS(ripCode);
8094
+ let jsCode;
8095
+ try {
8096
+ jsCode = compileToJS(ripCode);
8097
+ } catch (compileError) {
8098
+ console.error("Rip compile error:", compileError.message);
8099
+ console.error("Source:", ripCode);
8100
+ continue;
8101
+ }
8049
8102
  await (0, eval)(`(async()=>{
8050
8103
  ${jsCode}
8051
8104
  })()`);
8052
8105
  script.setAttribute("data-rip-processed", "true");
8053
8106
  } catch (error) {
8054
- console.error("Error compiling Rip script:", error);
8055
- console.error("Script content:", script.textContent);
8107
+ console.error("Rip runtime error:", error);
8056
8108
  }
8057
8109
  }
8058
8110
  }
8059
- if (typeof document !== "undefined") {
8060
- if (document.readyState === "loading") {
8061
- document.addEventListener("DOMContentLoaded", processRipScripts);
8062
- } else {
8063
- processRipScripts();
8064
- }
8065
- }
8066
8111
  async function importRip(url) {
8067
8112
  const source = await fetch(url).then((r) => {
8068
8113
  if (!r.ok)
@@ -8106,6 +8151,13 @@ if (typeof globalThis !== "undefined") {
8106
8151
  globalThis.importRip = importRip;
8107
8152
  globalThis.compileToJS = compileToJS;
8108
8153
  }
8154
+ if (typeof document !== "undefined") {
8155
+ if (document.readyState === "loading") {
8156
+ document.addEventListener("DOMContentLoaded", processRipScripts);
8157
+ } else {
8158
+ processRipScripts();
8159
+ }
8160
+ }
8109
8161
  export {
8110
8162
  rip,
8111
8163
  processRipScripts,