rip-lang 3.13.31 → 3.13.33

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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.31",
3
+ "version": "3.13.33",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
package/src/components.js CHANGED
@@ -528,25 +528,36 @@ export function installComponentSupport(CodeGenerator, Lexer) {
528
528
  * Recursively transform s-expression to replace member identifiers with this.X.value.
529
529
  * For component context where state variables are signals.
530
530
  */
531
+ const _str = (s) => typeof s === 'string' ? s : s instanceof String ? s.valueOf() : null;
532
+ const _transferMeta = (from, to) => {
533
+ if (!(from instanceof String)) return to;
534
+ const s = new String(to);
535
+ if (from.predicate) s.predicate = true;
536
+ if (from.await) s.await = true;
537
+ return (s.predicate || s.await) ? s : to;
538
+ };
539
+
531
540
  proto.transformComponentMembers = function(sexpr) {
532
541
  const self = this._self;
533
542
  if (!Array.isArray(sexpr)) {
534
- if (typeof sexpr === 'string' && this.reactiveMembers && this.reactiveMembers.has(sexpr)) {
535
- return ['.', ['.', self, sexpr], 'value'];
543
+ const sv = _str(sexpr);
544
+ if (sv && this.reactiveMembers && this.reactiveMembers.has(sv)) {
545
+ return ['.', ['.', self, sv], _transferMeta(sexpr, 'value')];
536
546
  }
537
- if (typeof sexpr === 'string' && this.componentMembers && this.componentMembers.has(sexpr)) {
538
- return ['.', self, sexpr];
547
+ if (sv && this.componentMembers && this.componentMembers.has(sv)) {
548
+ return ['.', self, _transferMeta(sexpr, sv)];
539
549
  }
540
550
  return sexpr;
541
551
  }
542
552
 
543
553
  // Special case: (. this memberName) for @member syntax
544
- if (sexpr[0] === '.' && sexpr[1] === 'this' && typeof sexpr[2] === 'string') {
545
- const memberName = sexpr[2];
554
+ if (sexpr[0] === '.' && sexpr[1] === 'this' && _str(sexpr[2]) != null) {
555
+ const prop = sexpr[2];
556
+ const memberName = _str(prop);
546
557
  if (this.reactiveMembers && this.reactiveMembers.has(memberName)) {
547
- return ['.', ['.', self, memberName], 'value'];
558
+ return ['.', ['.', self, memberName], _transferMeta(prop, 'value')];
548
559
  }
549
- return this._factoryMode ? ['.', self, sexpr[2]] : sexpr;
560
+ return this._factoryMode ? ['.', self, prop] : sexpr;
550
561
  }
551
562
 
552
563
  // Dot access: transform the object but not the property name
@@ -700,13 +711,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
700
711
  for (const { name, value, isPublic } of stateVars) {
701
712
  const val = this.generateInComponent(value, 'value');
702
713
  lines.push(isPublic
703
- ? ` this.${name} = __state(props.${name} ?? ${val});`
714
+ ? ` this.${name} = __state(props.__bind_${name}__ ?? props.${name} ?? ${val});`
704
715
  : ` this.${name} = __state(${val});`);
705
716
  }
706
717
 
707
718
  // Computed (derived)
708
719
  for (const { name, expr } of derivedVars) {
709
- if (this.is(expr, 'block') && expr.length > 2) {
720
+ if (this.is(expr, 'block')) {
710
721
  const transformed = this.transformComponentMembers(expr);
711
722
  const body = this.generateFunctionBody(transformed);
712
723
  lines.push(` this.${name} = __computed(() => ${body});`);
@@ -720,7 +731,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
720
731
  for (const effect of effects) {
721
732
  const effectBody = effect[2];
722
733
  const isAsync = this.containsAwait(effectBody) ? 'async ' : '';
723
- if (this.is(effectBody, 'block') && effectBody.length > 2) {
734
+ if (this.is(effectBody, 'block')) {
724
735
  const transformed = this.transformComponentMembers(effectBody);
725
736
  const body = this.generateFunctionBody(transformed, [], true);
726
737
  lines.push(` __effect(${isAsync}() => ${body});`);
@@ -925,6 +936,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
925
936
  this._pushEffect(`${textVar}.data = ${this._self}.${str}.value;`);
926
937
  return textVar;
927
938
  }
939
+ // Slot projection — bare <slot> tag → project @children
940
+ if (str === 'slot' && this.componentMembers) {
941
+ const s = this._self;
942
+ const slotVar = this.newElementVar('slot');
943
+ this._createLines.push(`${slotVar} = ${s}.children instanceof Node ? ${s}.children : (${s}.children != null ? document.createTextNode(String(${s}.children)) : document.createComment(''));`);
944
+ return slotVar;
945
+ }
928
946
  // Static tag without content (possibly with #id)
929
947
  const [tagStr, idStr] = str.split('#');
930
948
  const elVar = this.newElementVar();
@@ -952,6 +970,14 @@ export function installComponentSupport(CodeGenerator, Lexer) {
952
970
  return this.generateChildComponent(headStr, rest);
953
971
  }
954
972
 
973
+ // Slot projection — replace <slot> with @children in component render
974
+ if (headStr === 'slot' && this.componentMembers) {
975
+ const s = this._self;
976
+ const slotVar = this.newElementVar('slot');
977
+ this._createLines.push(`${slotVar} = ${s}.children instanceof Node ? ${s}.children : (${s}.children != null ? document.createTextNode(String(${s}.children)) : document.createComment(''));`);
978
+ return slotVar;
979
+ }
980
+
955
981
  // HTML tag (possibly with #id, e.g. div#content)
956
982
  if (headStr && this.isHtmlTag(headStr)) {
957
983
  let [tagName, id] = headStr.split('#');
@@ -1336,7 +1362,11 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1336
1362
  this._createLines.push(`if (${valueCode}) ${elVar}.setAttribute('${key}', '');`);
1337
1363
  }
1338
1364
  } else if (this.hasReactiveDeps(value)) {
1339
- this._pushEffect(`${elVar}.setAttribute('${key}', ${valueCode});`);
1365
+ if (Array.isArray(value) && value[0] === 'presence') {
1366
+ this._pushEffect(`{ const __v = ${valueCode}; __v == null ? ${elVar}.removeAttribute('${key}') : ${elVar}.setAttribute('${key}', __v); }`);
1367
+ } else {
1368
+ this._pushEffect(`${elVar}.setAttribute('${key}', ${valueCode});`);
1369
+ }
1340
1370
  } else {
1341
1371
  this._createLines.push(`${elVar}.setAttribute('${key}', ${valueCode});`);
1342
1372
  }
@@ -1636,13 +1666,18 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1636
1666
  this._pendingAutoWire = false;
1637
1667
  const instVar = this.newElementVar('inst');
1638
1668
  const elVar = this.newElementVar('el');
1639
- const { propsCode, reactiveProps, childrenSetupLines } = this.buildComponentProps(args);
1669
+ const { propsCode, reactiveProps, eventBindings, childrenSetupLines } = this.buildComponentProps(args);
1640
1670
 
1641
1671
  const s = this._self;
1642
1672
  this._createLines.push(`${instVar} = new ${componentName}(${propsCode});`);
1643
1673
  this._createLines.push(`${elVar} = ${instVar}._root = ${instVar}._create();`);
1644
1674
  this._createLines.push(`(${s}._children || (${s}._children = [])).push(${instVar});`);
1645
1675
 
1676
+ for (const { event, value } of eventBindings) {
1677
+ const handlerCode = this.generateInComponent(value, 'value');
1678
+ this._createLines.push(`${elVar}.addEventListener('${event}', (e) => __batch(() => (${handlerCode})(e)));`);
1679
+ }
1680
+
1646
1681
  this._setupLines.push(`try { if (${instVar}._setup) ${instVar}._setup(); if (${instVar}.mounted) ${instVar}.mounted(); } catch (__e) { __handleComponentError(__e, ${instVar}); }`);
1647
1682
 
1648
1683
  for (const { key, valueCode } of reactiveProps) {
@@ -1663,12 +1698,17 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1663
1698
  proto.buildComponentProps = function(args) {
1664
1699
  const props = [];
1665
1700
  const reactiveProps = [];
1701
+ const eventBindings = [];
1666
1702
  let childrenVar = null;
1667
1703
  const childrenSetupLines = [];
1668
1704
 
1669
1705
  // Simple reactive values pass the signal directly for shared reactivity;
1670
1706
  // complex expressions use normal .value unwrapping to compute the value.
1671
1707
  const addProp = (key, value) => {
1708
+ if (key.startsWith('@')) {
1709
+ eventBindings.push({ event: key.slice(1).split('.')[0], value });
1710
+ return;
1711
+ }
1672
1712
  const isDirectSignal = this.reactiveMembers && (
1673
1713
  (typeof value === 'string' && this.reactiveMembers.has(value)) ||
1674
1714
  (Array.isArray(value) && value[0] === '.' && value[1] === 'this' && typeof value[2] === 'string' && this.reactiveMembers.has(value[2]))
@@ -1688,7 +1728,11 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1688
1728
  const addObjectProps = (objExpr) => {
1689
1729
  for (let i = 1; i < objExpr.length; i++) {
1690
1730
  const [key, value] = objExpr[i];
1691
- if (typeof key === 'string') addProp(key, value);
1731
+ if (typeof key === 'string') {
1732
+ addProp(key, value);
1733
+ } else if (Array.isArray(key) && key[0] === '.' && key[1] === 'this' && typeof key[2] === 'string') {
1734
+ eventBindings.push({ event: key[2], value });
1735
+ }
1692
1736
  }
1693
1737
  };
1694
1738
 
@@ -1735,7 +1779,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1735
1779
  }
1736
1780
 
1737
1781
  const propsCode = props.length > 0 ? `{ ${props.join(', ')} }` : '{}';
1738
- return { propsCode, reactiveProps, childrenSetupLines };
1782
+ return { propsCode, reactiveProps, eventBindings, childrenSetupLines };
1739
1783
  };
1740
1784
 
1741
1785
  // --------------------------------------------------------------------------