ripple 0.3.8 → 0.3.10

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +2 -2
  3. package/src/compiler/phases/1-parse/index.js +38 -172
  4. package/src/compiler/phases/2-analyze/index.js +308 -115
  5. package/src/compiler/phases/2-analyze/prune.js +13 -5
  6. package/src/compiler/phases/3-transform/client/index.js +197 -213
  7. package/src/compiler/phases/3-transform/segments.js +0 -7
  8. package/src/compiler/phases/3-transform/server/index.js +77 -170
  9. package/src/compiler/types/acorn.d.ts +1 -1
  10. package/src/compiler/types/estree.d.ts +1 -1
  11. package/src/compiler/types/import.d.ts +0 -2
  12. package/src/compiler/types/index.d.ts +14 -18
  13. package/src/compiler/types/parse.d.ts +3 -9
  14. package/src/compiler/utils.js +154 -21
  15. package/src/runtime/element.js +39 -0
  16. package/src/runtime/index-client.js +2 -13
  17. package/src/runtime/index-server.js +2 -2
  18. package/src/runtime/internal/client/bindings.js +3 -1
  19. package/src/runtime/internal/client/composite.js +11 -6
  20. package/src/runtime/internal/client/events.js +1 -1
  21. package/src/runtime/internal/client/expression.js +218 -0
  22. package/src/runtime/internal/client/head.js +3 -4
  23. package/src/runtime/internal/client/index.js +4 -1
  24. package/src/runtime/internal/client/portal.js +12 -6
  25. package/src/runtime/internal/client/runtime.js +0 -52
  26. package/src/runtime/internal/server/index.js +57 -56
  27. package/tests/client/basic/basic.components.test.ripple +85 -87
  28. package/tests/client/basic/basic.errors.test.ripple +28 -4
  29. package/tests/client/basic/basic.reactivity.test.ripple +10 -155
  30. package/tests/client/basic/basic.rendering.test.ripple +23 -8
  31. package/tests/client/capture-error.js +12 -0
  32. package/tests/client/compiler/compiler.basic.test.ripple +107 -18
  33. package/tests/client/composite/composite.props.test.ripple +5 -9
  34. package/tests/client/composite/composite.reactivity.test.ripple +35 -36
  35. package/tests/client/composite/composite.render.test.ripple +45 -13
  36. package/tests/client/css/global-additional-cases.test.ripple +3 -3
  37. package/tests/client/dynamic-elements.test.ripple +3 -4
  38. package/tests/client/lazy-destructuring.test.ripple +69 -12
  39. package/tests/client/svg.test.ripple +4 -4
  40. package/tests/hydration/basic.test.js +23 -0
  41. package/tests/hydration/compiled/client/basic.js +118 -66
  42. package/tests/hydration/compiled/client/composite.js +90 -37
  43. package/tests/hydration/compiled/client/events.js +18 -18
  44. package/tests/hydration/compiled/client/for.js +62 -62
  45. package/tests/hydration/compiled/client/head.js +10 -10
  46. package/tests/hydration/compiled/client/hmr.js +13 -10
  47. package/tests/hydration/compiled/client/html.js +274 -236
  48. package/tests/hydration/compiled/client/if-children.js +41 -35
  49. package/tests/hydration/compiled/client/if.js +2 -2
  50. package/tests/hydration/compiled/client/mixed-control-flow.js +12 -12
  51. package/tests/hydration/compiled/client/nested-control-flow.js +46 -46
  52. package/tests/hydration/compiled/client/portal.js +8 -8
  53. package/tests/hydration/compiled/client/reactivity.js +14 -14
  54. package/tests/hydration/compiled/client/return.js +2 -2
  55. package/tests/hydration/compiled/client/try.js +4 -4
  56. package/tests/hydration/compiled/server/basic.js +64 -31
  57. package/tests/hydration/compiled/server/composite.js +62 -29
  58. package/tests/hydration/compiled/server/hmr.js +24 -37
  59. package/tests/hydration/compiled/server/html.js +472 -611
  60. package/tests/hydration/compiled/server/if-children.js +77 -103
  61. package/tests/hydration/compiled/server/portal.js +8 -8
  62. package/tests/hydration/components/basic.ripple +15 -5
  63. package/tests/hydration/components/composite.ripple +13 -1
  64. package/tests/hydration/components/hmr.ripple +1 -3
  65. package/tests/hydration/components/html.ripple +13 -35
  66. package/tests/hydration/components/if-children.ripple +4 -8
  67. package/tests/hydration/composite.test.js +11 -0
  68. package/tests/server/basic.attributes.test.ripple +50 -0
  69. package/tests/server/basic.components.test.ripple +22 -28
  70. package/tests/server/basic.test.ripple +12 -0
  71. package/tests/server/compiler.test.ripple +43 -4
  72. package/tests/server/composite.props.test.ripple +5 -9
  73. package/tests/server/dynamic-elements.test.ripple +3 -4
  74. package/tests/server/lazy-destructuring.test.ripple +68 -12
  75. package/tests/server/style-identifier.test.ripple +2 -4
  76. package/tsconfig.typecheck.json +4 -0
  77. package/types/index.d.ts +9 -21
  78. package/tests/client/__snapshots__/tracked-expression.test.ripple.snap +0 -34
  79. package/tests/client/tracked-expression.test.ripple +0 -26
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # ripple
2
2
 
3
+ ## 0.3.10
4
+
5
+ ### Patch Changes
6
+
7
+ - [`aef1253`](https://github.com/Ripple-TS/ripple/commit/aef1253dd79c067a8358172d502dc21d8a9a9085)
8
+ Thanks [@trueadm](https://github.com/trueadm)! - Replace `<children />` with
9
+ `{children}` expression syntax for rendering component children
10
+
11
+ - Updated dependencies
12
+ [[`aef1253`](https://github.com/Ripple-TS/ripple/commit/aef1253dd79c067a8358172d502dc21d8a9a9085)]:
13
+ - ripple@0.3.10
14
+
15
+ ## 0.3.9
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies []:
20
+ - ripple@0.3.9
21
+
3
22
  ## 0.3.8
4
23
 
5
24
  ### Patch Changes
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Ripple is an elegant TypeScript UI framework",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.3.8",
6
+ "version": "0.3.10",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index-client.js",
9
9
  "main": "src/runtime/index-client.js",
@@ -105,6 +105,6 @@
105
105
  "vscode-languageserver-types": "^3.17.5"
106
106
  },
107
107
  "peerDependencies": {
108
- "ripple": "0.3.8"
108
+ "ripple": "0.3.10"
109
109
  }
110
110
  }
@@ -413,7 +413,9 @@ function RipplePlugin(config) {
413
413
  /** @type {AST.Property} */ (prop).method = true;
414
414
  /** @type {AST.Property} */ (prop).kind = 'init';
415
415
  /** @type {AST.Property} */ (prop).value = this.parseMethod(false, false);
416
- /** @type {AST.Property} */ (prop).value.typeParameters = typeParameters;
416
+ /** @type {AST.FunctionExpression} */ (
417
+ /** @type {AST.Property} */ (prop).value
418
+ ).typeParameters = typeParameters;
417
419
  return;
418
420
  }
419
421
  }
@@ -666,99 +668,9 @@ function RipplePlugin(config) {
666
668
  }
667
669
  }
668
670
  }
669
- if (code === 64) {
670
- // @ character
671
- // Look ahead to see if this is followed by an opening paren
672
- if (this.pos + 1 < this.input.length) {
673
- const nextChar = this.input.charCodeAt(this.pos + 1);
674
-
675
- // Check if this is @( for unboxing expression syntax
676
- if (nextChar === 40) {
677
- // ( character
678
- this.pos += 2; // skip '@('
679
- return this.finishToken(tt.parenL, '@(');
680
- }
681
- }
682
- }
683
671
  return super.getTokenFromCode(code);
684
672
  }
685
673
 
686
- /**
687
- * Override parseSubscripts to handle `.@[expression]` syntax for reactive computed member access
688
- * @type {Parse.Parser['parseSubscripts']}
689
- */
690
- parseSubscripts(
691
- base,
692
- startPos,
693
- startLoc,
694
- noCalls,
695
- maybeAsyncArrow,
696
- optionalChained,
697
- forInit,
698
- ) {
699
- // Check for `.@[` pattern for reactive computed member access
700
- const isDotOrOptional = this.type === tt.dot || this.type === tt.questionDot;
701
-
702
- if (isDotOrOptional) {
703
- // Check the next two characters without consuming tokens
704
- // this.pos currently points AFTER the dot token
705
- const nextChar = this.input.charCodeAt(this.pos);
706
- const charAfter = this.input.charCodeAt(this.pos + 1);
707
-
708
- // Check for @[ pattern (@ = 64, [ = 91)
709
- if (nextChar === 64 && charAfter === 91) {
710
- const node = /** @type {AST.MemberExpression} */ (this.startNodeAt(startPos, startLoc));
711
- node.object = base;
712
- node.computed = true;
713
- node.optional = this.type === tt.questionDot;
714
- node.tracked = true;
715
-
716
- // Consume the dot/questionDot token
717
- this.next();
718
-
719
- // Manually skip the @ character
720
- this.pos += 1;
721
-
722
- // Now call finishToken to properly consume the [ bracket
723
- this.finishToken(tt.bracketL);
724
-
725
- // Now we're positioned correctly to parse the expression
726
- this.next(); // Move to first token inside brackets
727
-
728
- // Parse the expression inside brackets
729
- node.property = this.parseExpression();
730
-
731
- // Expect closing bracket
732
- this.expect(tt.bracketR);
733
-
734
- // Finish this MemberExpression node
735
- base = /** @type {AST.MemberExpression} */ (this.finishNode(node, 'MemberExpression'));
736
-
737
- // Recursively handle any further subscripts (chaining)
738
- return this.parseSubscripts(
739
- base,
740
- startPos,
741
- startLoc,
742
- noCalls,
743
- maybeAsyncArrow,
744
- optionalChained,
745
- forInit,
746
- );
747
- }
748
- }
749
-
750
- // Fall back to default parseSubscripts implementation
751
- return super.parseSubscripts(
752
- base,
753
- startPos,
754
- startLoc,
755
- noCalls,
756
- maybeAsyncArrow,
757
- optionalChained,
758
- forInit,
759
- );
760
- }
761
-
762
674
  /**
763
675
  * Override isLet to recognize `let &{` and `let &[` as variable declarations.
764
676
  * Acorn's isLet checks the char after `let` and only recognizes `{`, `[`, or identifiers.
@@ -795,7 +707,7 @@ function RipplePlugin(config) {
795
707
  // & directly followed by { or [ — lazy destructuring
796
708
  this.next(); // consume &, now current token is { or [
797
709
  const pattern = super.parseBindingAtom();
798
- pattern.lazy = true;
710
+ /** @type {AST.ObjectPattern | AST.ArrayPattern} */ (pattern).lazy = true;
799
711
  return pattern;
800
712
  }
801
713
  }
@@ -810,11 +722,6 @@ function RipplePlugin(config) {
810
722
  const lookahead_type = this.lookahead().type;
811
723
  const is_next_call_token = lookahead_type === tt.parenL || lookahead_type === tt.relational;
812
724
 
813
- // Check if this is @(expression) for unboxing tracked values
814
- if (this.type === tt.parenL && this.value === '@(') {
815
- return this.parseTrackedExpression();
816
- }
817
-
818
725
  // Check if this is #server identifier for server function calls
819
726
  if (this.type === tt.name && this.value === '#server') {
820
727
  const node = this.startNode();
@@ -855,31 +762,6 @@ function RipplePlugin(config) {
855
762
  return expr;
856
763
  }
857
764
 
858
- /**
859
- * Parse `@(expression)` syntax for unboxing tracked values
860
- * Creates a TrackedExpression node with the argument property
861
- * @type {Parse.Parser['parseTrackedExpression']}
862
- */
863
- parseTrackedExpression() {
864
- const node = /** @type {AST.TrackedExpression} */ (this.startNode());
865
- this.next(); // consume '@(' token
866
- node.argument = this.parseExpression();
867
- this.expect(tt.parenR); // expect ')'
868
- return this.finishNode(node, 'TrackedExpression');
869
- }
870
-
871
- /**
872
- * Override to allow TrackedExpression as a valid lvalue for update expressions
873
- * @type {Parse.Parser['checkLValSimple']}
874
- */
875
- checkLValSimple(expr, bindingType, checkClashes) {
876
- // Allow TrackedExpression as a valid lvalue for ++/-- operators
877
- if (expr.type === 'TrackedExpression') {
878
- return;
879
- }
880
- return super.checkLValSimple(expr, bindingType, checkClashes);
881
- }
882
-
883
765
  /**
884
766
  * Override checkLocalExport to check all scopes in the scope stack.
885
767
  * This is needed because server blocks create nested scopes, but exports
@@ -1018,6 +900,7 @@ function RipplePlugin(config) {
1018
900
  return this.parseFor(node, null);
1019
901
  }
1020
902
 
903
+ // @ts-ignore — acorn internal: isLet accepts 0 args at runtime
1021
904
  let isLet = this.isLet();
1022
905
  if (this.type === tt._var || this.type === tt._const || isLet) {
1023
906
  let init = /** @type {AST.VariableDeclaration} */ (this.startNode()),
@@ -1060,7 +943,9 @@ function RipplePlugin(config) {
1060
943
  }
1061
944
 
1062
945
  let containsEsc = this.containsEsc;
1063
- let refDestructuringErrors = new DestructuringErrors();
946
+ let refDestructuringErrors = new /** @type {new () => Parse.DestructuringErrors} */ (
947
+ /** @type {unknown} */ (DestructuringErrors)
948
+ )();
1064
949
  let initPos = this.start;
1065
950
  let init_expr =
1066
951
  awaitAt > -1
@@ -1230,7 +1115,7 @@ function RipplePlugin(config) {
1230
1115
  let node = /** @type {ESTreeJSX.JSXExpressionContainer} */ (this.startNode());
1231
1116
  this.next();
1232
1117
 
1233
- if (this.value === 'html') {
1118
+ if (this.type === tt.name && this.value === 'html') {
1234
1119
  node.html = true;
1235
1120
  this.next();
1236
1121
  if (this.type === tt.braceR) {
@@ -1239,6 +1124,15 @@ function RipplePlugin(config) {
1239
1124
  '"html" is a Ripple keyword and must be used in the form {html some_content}',
1240
1125
  );
1241
1126
  }
1127
+ } else if (this.type === tt.name && this.value === 'text') {
1128
+ node.text = true;
1129
+ this.next();
1130
+ if (this.type === tt.braceR) {
1131
+ this.raise(
1132
+ this.start,
1133
+ '"text" is a Ripple keyword and must be used in the form {text some_value}',
1134
+ );
1135
+ }
1242
1136
  }
1243
1137
 
1244
1138
  node.expression =
@@ -1391,38 +1285,8 @@ function RipplePlugin(config) {
1391
1285
  )
1392
1286
  );
1393
1287
  memberExpr.object = node;
1394
-
1395
- // Check for .@[expression] syntax for tracked computed member access
1396
- // After eating the dot, check if the current token is @ followed by [
1397
- if (this.type.label === '@') {
1398
- // Check if the next character after @ is [
1399
- const nextChar = this.input.charCodeAt(this.pos);
1400
-
1401
- if (nextChar === 91) {
1402
- // [ character
1403
- memberExpr.computed = true;
1404
-
1405
- // Consume the @ token
1406
- this.next();
1407
-
1408
- // Now this.type should be bracketL
1409
- // Consume the [ and parse the expression inside
1410
- this.expect(tt.bracketL);
1411
-
1412
- // Parse the expression inside brackets
1413
- memberExpr.property = /** @type {ESTreeJSX.JSXIdentifier} */ (this.parseExpression());
1414
- /** @type {AST.TrackedNode} */ (memberExpr.property).tracked = true;
1415
-
1416
- // Expect closing bracket
1417
- this.expect(tt.bracketR);
1418
- } else {
1419
- this.unexpected();
1420
- }
1421
- } else {
1422
- // Regular dot notation
1423
- memberExpr.property = this.jsx_parseIdentifier();
1424
- memberExpr.computed = false;
1425
- }
1288
+ memberExpr.property = this.jsx_parseIdentifier();
1289
+ memberExpr.computed = false;
1426
1290
  memberExpr = this.finishNode(memberExpr, 'JSXMemberExpression');
1427
1291
  while (this.eat(tt.dot)) {
1428
1292
  let newMemberExpr = /** @type {ESTreeJSX.JSXMemberExpression} */ (
@@ -1473,16 +1337,12 @@ function RipplePlugin(config) {
1473
1337
  const clause = /** @type {AST.CatchClause} */ (this.startNode());
1474
1338
  this.next();
1475
1339
  if (this.eat(tt.parenL)) {
1476
- clause.param = this.parseCatchClauseParam();
1340
+ clause.param = this.parseBindingAtom();
1341
+ this.expect(tt.parenR);
1477
1342
  } else {
1478
- if (this.options.ecmaVersion < 10) {
1479
- this.unexpected();
1480
- }
1481
1343
  clause.param = null;
1482
- this.enterScope(0);
1483
1344
  }
1484
- clause.body = this.parseBlock(false);
1485
- this.exitScope();
1345
+ clause.body = this.parseBlock();
1486
1346
  node.handler = this.finishNode(clause, 'CatchClause');
1487
1347
  }
1488
1348
  node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null;
@@ -1707,8 +1567,9 @@ function RipplePlugin(config) {
1707
1567
  if (expression.type === 'Literal') {
1708
1568
  expression.was_expression = true;
1709
1569
  }
1710
- /** @type {ESTreeJSX.JSXExpressionContainer['expression']} */ (attr.value) =
1711
- expression;
1570
+ // @ts-ignore intentional AST node conversion from JSX to Ripple
1571
+ /** @type {ESTreeJSX.JSXAttribute} */ (attr).value =
1572
+ /** @type {ESTreeJSX.JSXExpressionContainer['expression']} */ (expression);
1712
1573
  }
1713
1574
  }
1714
1575
  }
@@ -2053,12 +1914,13 @@ function RipplePlugin(config) {
2053
1914
  if (this.type === tt.braceL) {
2054
1915
  const node = this.jsx_parseExpressionContainer();
2055
1916
  // Keep JSXEmptyExpression as-is (for prettier to handle comments)
2056
- // but convert other expressions to Text/Html nodes
1917
+ // but convert other expressions to Html/RippleExpression/Text nodes
2057
1918
  if (node.expression.type !== 'JSXEmptyExpression') {
2058
- /** @type {AST.Html | AST.TextNode} */ (/** @type {unknown} */ (node)).type = node.html
2059
- ? 'Html'
2060
- : 'Text';
1919
+ /** @type {AST.RippleExpression | AST.Html | AST.TextNode} */ (
1920
+ /** @type {unknown} */ (node)
1921
+ ).type = node.html ? 'Html' : node.text ? 'Text' : 'RippleExpression';
2061
1922
  delete node.html;
1923
+ delete node.text;
2062
1924
  }
2063
1925
  body.push(node);
2064
1926
  } else if (this.type === tt.braceR) {
@@ -2194,12 +2056,16 @@ function RipplePlugin(config) {
2194
2056
  this.context.some((c) => c === tstc.tc_expr)
2195
2057
  ) {
2196
2058
  const node = this.jsx_parseExpressionContainer();
2197
- // Keep JSXEmptyExpression as-is (don't convert to Text)
2059
+ // Keep JSXEmptyExpression as-is (don't convert to RippleExpression/Text/Html)
2198
2060
  if (node.expression.type !== 'JSXEmptyExpression') {
2199
- /** @type {AST.TextNode} */ (/** @type {unknown} */ (node)).type = 'Text';
2061
+ /** @type {AST.RippleExpression | AST.Html | AST.TextNode} */ (
2062
+ /** @type {unknown} */ (node)
2063
+ ).type = node.html ? 'Html' : node.text ? 'Text' : 'RippleExpression';
2064
+ delete node.html;
2065
+ delete node.text;
2200
2066
  }
2201
2067
 
2202
- return /** @type {ESTreeJSX.JSXEmptyExpression | AST.TextNode | ESTreeJSX.JSXExpressionContainer} */ (
2068
+ return /** @type {ESTreeJSX.JSXEmptyExpression | AST.RippleExpression | AST.Html | AST.TextNode | ESTreeJSX.JSXExpressionContainer} */ (
2203
2069
  /** @type {unknown} */ (node)
2204
2070
  );
2205
2071
  }