ripple 0.3.7 → 0.3.9
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 +14 -0
- package/package.json +2 -2
- package/src/compiler/phases/1-parse/index.js +48 -349
- package/src/compiler/phases/2-analyze/index.js +343 -52
- package/src/compiler/phases/3-transform/client/index.js +28 -160
- package/src/compiler/phases/3-transform/segments.js +0 -7
- package/src/compiler/phases/3-transform/server/index.js +31 -154
- package/src/compiler/types/acorn.d.ts +1 -1
- package/src/compiler/types/estree.d.ts +1 -1
- package/src/compiler/types/import.d.ts +0 -2
- package/src/compiler/types/index.d.ts +5 -17
- package/src/compiler/types/parse.d.ts +1 -17
- package/src/compiler/utils.js +53 -20
- package/src/runtime/index-client.js +2 -13
- package/src/runtime/index-server.js +2 -2
- package/src/runtime/internal/client/bindings.js +3 -1
- package/src/runtime/internal/client/composite.js +3 -2
- package/src/runtime/internal/client/events.js +1 -1
- package/src/runtime/internal/client/head.js +3 -4
- package/src/runtime/internal/client/index.js +0 -1
- package/src/runtime/internal/client/runtime.js +0 -52
- package/src/runtime/internal/server/index.js +31 -55
- package/tests/client/array/array.copy-within.test.ripple +12 -12
- package/tests/client/array/array.derived.test.ripple +46 -46
- package/tests/client/array/array.iteration.test.ripple +10 -10
- package/tests/client/array/array.mutations.test.ripple +20 -20
- package/tests/client/array/array.to-methods.test.ripple +6 -6
- package/tests/client/async-suspend.test.ripple +5 -5
- package/tests/client/basic/basic.attributes.test.ripple +81 -81
- package/tests/client/basic/basic.collections.test.ripple +9 -9
- package/tests/client/basic/basic.components.test.ripple +28 -28
- package/tests/client/basic/basic.errors.test.ripple +46 -18
- package/tests/client/basic/basic.events.test.ripple +37 -37
- package/tests/client/basic/basic.get-set.test.ripple +6 -6
- package/tests/client/basic/basic.reactivity.test.ripple +58 -203
- package/tests/client/basic/basic.rendering.test.ripple +19 -19
- package/tests/client/basic/basic.utilities.test.ripple +3 -3
- package/tests/client/boundaries.test.ripple +12 -12
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.ripple.snap +5 -5
- package/tests/client/compiler/compiler.assignments.test.ripple +19 -19
- package/tests/client/compiler/compiler.basic.test.ripple +46 -27
- package/tests/client/compiler/compiler.tracked-access.test.ripple +2 -2
- package/tests/client/composite/composite.dynamic-components.test.ripple +9 -9
- package/tests/client/composite/composite.props.test.ripple +14 -16
- package/tests/client/composite/composite.reactivity.test.ripple +69 -70
- package/tests/client/composite/composite.render.test.ripple +3 -3
- package/tests/client/computed-properties.test.ripple +4 -4
- package/tests/client/date.test.ripple +42 -42
- package/tests/client/dynamic-elements.test.ripple +44 -45
- package/tests/client/events.test.ripple +70 -70
- package/tests/client/for.test.ripple +25 -25
- package/tests/client/head.test.ripple +19 -19
- package/tests/client/html.test.ripple +3 -3
- package/tests/client/input-value.test.ripple +84 -84
- package/tests/client/lazy-destructuring.test.ripple +138 -26
- package/tests/client/map.test.ripple +16 -16
- package/tests/client/media-query.test.ripple +7 -7
- package/tests/client/portal.test.ripple +11 -11
- package/tests/client/ref.test.ripple +4 -4
- package/tests/client/return.test.ripple +52 -52
- package/tests/client/set.test.ripple +6 -6
- package/tests/client/svg.test.ripple +5 -5
- package/tests/client/switch.test.ripple +44 -44
- package/tests/client/try.test.ripple +5 -5
- package/tests/client/url/url.derived.test.ripple +6 -6
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +8 -8
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +10 -10
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +10 -10
- package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +18 -18
- package/tests/client/url-search-params/url-search-params.serialization.test.ripple +2 -2
- package/tests/hydration/compiled/client/events.js +25 -25
- package/tests/hydration/compiled/client/for.js +70 -66
- package/tests/hydration/compiled/client/head.js +25 -25
- package/tests/hydration/compiled/client/hmr.js +2 -2
- package/tests/hydration/compiled/client/html.js +3 -3
- package/tests/hydration/compiled/client/if-children.js +24 -24
- package/tests/hydration/compiled/client/if.js +18 -18
- package/tests/hydration/compiled/client/mixed-control-flow.js +9 -9
- package/tests/hydration/compiled/client/portal.js +3 -3
- package/tests/hydration/compiled/client/reactivity.js +16 -16
- package/tests/hydration/compiled/client/return.js +40 -40
- package/tests/hydration/compiled/client/switch.js +12 -12
- package/tests/hydration/compiled/server/events.js +19 -19
- package/tests/hydration/compiled/server/for.js +41 -41
- package/tests/hydration/compiled/server/head.js +26 -26
- package/tests/hydration/compiled/server/hmr.js +2 -2
- package/tests/hydration/compiled/server/html.js +2 -2
- package/tests/hydration/compiled/server/if-children.js +16 -16
- package/tests/hydration/compiled/server/if.js +11 -11
- package/tests/hydration/compiled/server/mixed-control-flow.js +6 -6
- package/tests/hydration/compiled/server/portal.js +2 -2
- package/tests/hydration/compiled/server/reactivity.js +16 -16
- package/tests/hydration/compiled/server/return.js +25 -25
- package/tests/hydration/compiled/server/switch.js +8 -8
- package/tests/hydration/components/events.ripple +25 -25
- package/tests/hydration/components/for.ripple +66 -66
- package/tests/hydration/components/head.ripple +16 -16
- package/tests/hydration/components/hmr.ripple +2 -2
- package/tests/hydration/components/html.ripple +3 -3
- package/tests/hydration/components/if-children.ripple +24 -24
- package/tests/hydration/components/if.ripple +18 -18
- package/tests/hydration/components/mixed-control-flow.ripple +9 -9
- package/tests/hydration/components/portal.ripple +3 -3
- package/tests/hydration/components/reactivity.ripple +16 -16
- package/tests/hydration/components/return.ripple +40 -40
- package/tests/hydration/components/switch.ripple +20 -20
- package/tests/server/await.test.ripple +3 -3
- package/tests/server/basic.attributes.test.ripple +34 -34
- package/tests/server/basic.components.test.ripple +10 -10
- package/tests/server/basic.test.ripple +38 -40
- package/tests/server/compiler.test.ripple +22 -0
- package/tests/server/composite.props.test.ripple +12 -14
- package/tests/server/dynamic-elements.test.ripple +15 -15
- package/tests/server/head.test.ripple +11 -11
- package/tests/server/lazy-destructuring.test.ripple +92 -13
- package/tsconfig.typecheck.json +4 -0
- package/types/index.d.ts +0 -19
- package/tests/client/__snapshots__/tracked-expression.test.ripple.snap +0 -34
- package/tests/client/tracked-expression.test.ripple +0 -26
package/CHANGELOG.md
CHANGED
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.
|
|
6
|
+
"version": "0.3.9",
|
|
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.
|
|
108
|
+
"ripple": "0.3.9"
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -52,16 +52,6 @@ function DestructuringErrors() {
|
|
|
52
52
|
return this;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/**
|
|
56
|
-
* @param {AST.Identifier | ESTreeJSX.JSXIdentifier} node
|
|
57
|
-
* @param {string} name
|
|
58
|
-
*/
|
|
59
|
-
function set_tracked_name(node, name) {
|
|
60
|
-
node.name = name.slice(1);
|
|
61
|
-
node.metadata ??= { path: [] };
|
|
62
|
-
node.metadata.source_name = name;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
55
|
/**
|
|
66
56
|
* Convert JSX node types to regular JavaScript node types
|
|
67
57
|
* @param {ESTreeJSX.JSXIdentifier | ESTreeJSX.JSXMemberExpression | AST.Node} node - The JSX node to convert
|
|
@@ -423,7 +413,9 @@ function RipplePlugin(config) {
|
|
|
423
413
|
/** @type {AST.Property} */ (prop).method = true;
|
|
424
414
|
/** @type {AST.Property} */ (prop).kind = 'init';
|
|
425
415
|
/** @type {AST.Property} */ (prop).value = this.parseMethod(false, false);
|
|
426
|
-
/** @type {AST.
|
|
416
|
+
/** @type {AST.FunctionExpression} */ (
|
|
417
|
+
/** @type {AST.Property} */ (prop).value
|
|
418
|
+
).typeParameters = typeParameters;
|
|
427
419
|
return;
|
|
428
420
|
}
|
|
429
421
|
}
|
|
@@ -676,202 +668,9 @@ function RipplePlugin(config) {
|
|
|
676
668
|
}
|
|
677
669
|
}
|
|
678
670
|
}
|
|
679
|
-
if (code === 64) {
|
|
680
|
-
// @ character
|
|
681
|
-
// Look ahead to see if this is followed by a valid identifier character or opening paren
|
|
682
|
-
if (this.pos + 1 < this.input.length) {
|
|
683
|
-
const nextChar = this.input.charCodeAt(this.pos + 1);
|
|
684
|
-
|
|
685
|
-
// Check if this is @( for unboxing expression syntax
|
|
686
|
-
if (nextChar === 40) {
|
|
687
|
-
// ( character
|
|
688
|
-
this.pos += 2; // skip '@('
|
|
689
|
-
return this.finishToken(tt.parenL, '@(');
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// Check if the next character can start an identifier
|
|
693
|
-
if (
|
|
694
|
-
(nextChar >= 65 && nextChar <= 90) || // A-Z
|
|
695
|
-
(nextChar >= 97 && nextChar <= 122) || // a-z
|
|
696
|
-
nextChar === 95 ||
|
|
697
|
-
nextChar === 36
|
|
698
|
-
) {
|
|
699
|
-
// _ or $
|
|
700
|
-
|
|
701
|
-
// Check if we're in an expression context
|
|
702
|
-
// In JSX expressions, inside parentheses, assignments, etc.
|
|
703
|
-
// we want to treat @ as an identifier prefix rather than decorator
|
|
704
|
-
const currentType = this.type;
|
|
705
|
-
/**
|
|
706
|
-
* @param {Parse.TokenType} type
|
|
707
|
-
* @param {Parse.Parser} parser
|
|
708
|
-
* @param {Parse.TokTypes} tt
|
|
709
|
-
* @returns {boolean}
|
|
710
|
-
*/
|
|
711
|
-
function inExpression(type, parser, tt) {
|
|
712
|
-
return (
|
|
713
|
-
parser.exprAllowed ||
|
|
714
|
-
type === tt.braceL || // Inside { }
|
|
715
|
-
type === tt.parenL || // Inside ( )
|
|
716
|
-
type === tt.eq || // After =
|
|
717
|
-
type === tt.comma || // After ,
|
|
718
|
-
type === tt.colon || // After :
|
|
719
|
-
type === tt.question || // After ?
|
|
720
|
-
type === tt.logicalOR || // After ||
|
|
721
|
-
type === tt.logicalAND || // After &&
|
|
722
|
-
type === tt.dot || // After . (for member expressions like obj.@prop)
|
|
723
|
-
type === tt.questionDot // After ?. (for optional chaining like obj?.@prop)
|
|
724
|
-
);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
/**
|
|
728
|
-
* @param {Parse.Parser} parser
|
|
729
|
-
* @param {Parse.TokTypes} tt
|
|
730
|
-
* @returns {boolean}
|
|
731
|
-
*/
|
|
732
|
-
function inAwait(parser, tt) {
|
|
733
|
-
return currentType === tt.name &&
|
|
734
|
-
parser.value === 'await' &&
|
|
735
|
-
parser.canAwait &&
|
|
736
|
-
parser.preToken
|
|
737
|
-
? inExpression(parser.preToken, parser, tt)
|
|
738
|
-
: false;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
if (inExpression(currentType, this, tt) || inAwait(this, tt)) {
|
|
742
|
-
return this.readAtIdentifier();
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
671
|
return super.getTokenFromCode(code);
|
|
748
672
|
}
|
|
749
673
|
|
|
750
|
-
/**
|
|
751
|
-
* Read an @ prefixed identifier
|
|
752
|
-
* @type {Parse.Parser['readAtIdentifier']}
|
|
753
|
-
*/
|
|
754
|
-
readAtIdentifier() {
|
|
755
|
-
const start = this.pos;
|
|
756
|
-
this.pos++; // skip '@'
|
|
757
|
-
|
|
758
|
-
// Read the identifier part manually
|
|
759
|
-
let word = '';
|
|
760
|
-
while (this.pos < this.input.length) {
|
|
761
|
-
const ch = this.input.charCodeAt(this.pos);
|
|
762
|
-
if (
|
|
763
|
-
(ch >= 65 && ch <= 90) || // A-Z
|
|
764
|
-
(ch >= 97 && ch <= 122) || // a-z
|
|
765
|
-
(ch >= 48 && ch <= 57) || // 0-9
|
|
766
|
-
ch === 95 ||
|
|
767
|
-
ch === 36
|
|
768
|
-
) {
|
|
769
|
-
// _ or $
|
|
770
|
-
word += this.input[this.pos++];
|
|
771
|
-
} else {
|
|
772
|
-
break;
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
if (word === '') {
|
|
777
|
-
this.raise(start, 'Invalid @ identifier');
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// Return the full identifier including @
|
|
781
|
-
return this.finishToken(tt.name, '@' + word);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
/**
|
|
785
|
-
* Override parseIdent to mark @ identifiers as tracked
|
|
786
|
-
* @type {Parse.Parser['parseIdent']}
|
|
787
|
-
*/
|
|
788
|
-
parseIdent(liberal) {
|
|
789
|
-
const node = /** @type {AST.Identifier &AST.NodeWithLocation} */ (
|
|
790
|
-
super.parseIdent(liberal)
|
|
791
|
-
);
|
|
792
|
-
if (node.name && node.name.startsWith('@')) {
|
|
793
|
-
set_tracked_name(node, node.name);
|
|
794
|
-
node.tracked = true;
|
|
795
|
-
}
|
|
796
|
-
return node;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
/**
|
|
800
|
-
* Override parseSubscripts to handle `.@[expression]` syntax for reactive computed member access
|
|
801
|
-
* @type {Parse.Parser['parseSubscripts']}
|
|
802
|
-
*/
|
|
803
|
-
parseSubscripts(
|
|
804
|
-
base,
|
|
805
|
-
startPos,
|
|
806
|
-
startLoc,
|
|
807
|
-
noCalls,
|
|
808
|
-
maybeAsyncArrow,
|
|
809
|
-
optionalChained,
|
|
810
|
-
forInit,
|
|
811
|
-
) {
|
|
812
|
-
// Check for `.@[` pattern for reactive computed member access
|
|
813
|
-
const isDotOrOptional = this.type === tt.dot || this.type === tt.questionDot;
|
|
814
|
-
|
|
815
|
-
if (isDotOrOptional) {
|
|
816
|
-
// Check the next two characters without consuming tokens
|
|
817
|
-
// this.pos currently points AFTER the dot token
|
|
818
|
-
const nextChar = this.input.charCodeAt(this.pos);
|
|
819
|
-
const charAfter = this.input.charCodeAt(this.pos + 1);
|
|
820
|
-
|
|
821
|
-
// Check for @[ pattern (@ = 64, [ = 91)
|
|
822
|
-
if (nextChar === 64 && charAfter === 91) {
|
|
823
|
-
const node = /** @type {AST.MemberExpression} */ (this.startNodeAt(startPos, startLoc));
|
|
824
|
-
node.object = base;
|
|
825
|
-
node.computed = true;
|
|
826
|
-
node.optional = this.type === tt.questionDot;
|
|
827
|
-
node.tracked = true;
|
|
828
|
-
|
|
829
|
-
// Consume the dot/questionDot token
|
|
830
|
-
this.next();
|
|
831
|
-
|
|
832
|
-
// Manually skip the @ character
|
|
833
|
-
this.pos += 1;
|
|
834
|
-
|
|
835
|
-
// Now call finishToken to properly consume the [ bracket
|
|
836
|
-
this.finishToken(tt.bracketL);
|
|
837
|
-
|
|
838
|
-
// Now we're positioned correctly to parse the expression
|
|
839
|
-
this.next(); // Move to first token inside brackets
|
|
840
|
-
|
|
841
|
-
// Parse the expression inside brackets
|
|
842
|
-
node.property = this.parseExpression();
|
|
843
|
-
|
|
844
|
-
// Expect closing bracket
|
|
845
|
-
this.expect(tt.bracketR);
|
|
846
|
-
|
|
847
|
-
// Finish this MemberExpression node
|
|
848
|
-
base = /** @type {AST.MemberExpression} */ (this.finishNode(node, 'MemberExpression'));
|
|
849
|
-
|
|
850
|
-
// Recursively handle any further subscripts (chaining)
|
|
851
|
-
return this.parseSubscripts(
|
|
852
|
-
base,
|
|
853
|
-
startPos,
|
|
854
|
-
startLoc,
|
|
855
|
-
noCalls,
|
|
856
|
-
maybeAsyncArrow,
|
|
857
|
-
optionalChained,
|
|
858
|
-
forInit,
|
|
859
|
-
);
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
// Fall back to default parseSubscripts implementation
|
|
864
|
-
return super.parseSubscripts(
|
|
865
|
-
base,
|
|
866
|
-
startPos,
|
|
867
|
-
startLoc,
|
|
868
|
-
noCalls,
|
|
869
|
-
maybeAsyncArrow,
|
|
870
|
-
optionalChained,
|
|
871
|
-
forInit,
|
|
872
|
-
);
|
|
873
|
-
}
|
|
874
|
-
|
|
875
674
|
/**
|
|
876
675
|
* Override isLet to recognize `let &{` and `let &[` as variable declarations.
|
|
877
676
|
* Acorn's isLet checks the char after `let` and only recognizes `{`, `[`, or identifiers.
|
|
@@ -908,7 +707,7 @@ function RipplePlugin(config) {
|
|
|
908
707
|
// & directly followed by { or [ — lazy destructuring
|
|
909
708
|
this.next(); // consume &, now current token is { or [
|
|
910
709
|
const pattern = super.parseBindingAtom();
|
|
911
|
-
pattern.lazy = true;
|
|
710
|
+
/** @type {AST.ObjectPattern | AST.ArrayPattern} */ (pattern).lazy = true;
|
|
912
711
|
return pattern;
|
|
913
712
|
}
|
|
914
713
|
}
|
|
@@ -923,11 +722,6 @@ function RipplePlugin(config) {
|
|
|
923
722
|
const lookahead_type = this.lookahead().type;
|
|
924
723
|
const is_next_call_token = lookahead_type === tt.parenL || lookahead_type === tt.relational;
|
|
925
724
|
|
|
926
|
-
// Check if this is @(expression) for unboxing tracked values
|
|
927
|
-
if (this.type === tt.parenL && this.value === '@(') {
|
|
928
|
-
return this.parseTrackedExpression();
|
|
929
|
-
}
|
|
930
|
-
|
|
931
725
|
// Check if this is #server identifier for server function calls
|
|
932
726
|
if (this.type === tt.name && this.value === '#server') {
|
|
933
727
|
const node = this.startNode();
|
|
@@ -968,31 +762,6 @@ function RipplePlugin(config) {
|
|
|
968
762
|
return expr;
|
|
969
763
|
}
|
|
970
764
|
|
|
971
|
-
/**
|
|
972
|
-
* Parse `@(expression)` syntax for unboxing tracked values
|
|
973
|
-
* Creates a TrackedExpression node with the argument property
|
|
974
|
-
* @type {Parse.Parser['parseTrackedExpression']}
|
|
975
|
-
*/
|
|
976
|
-
parseTrackedExpression() {
|
|
977
|
-
const node = /** @type {AST.TrackedExpression} */ (this.startNode());
|
|
978
|
-
this.next(); // consume '@(' token
|
|
979
|
-
node.argument = this.parseExpression();
|
|
980
|
-
this.expect(tt.parenR); // expect ')'
|
|
981
|
-
return this.finishNode(node, 'TrackedExpression');
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
/**
|
|
985
|
-
* Override to allow TrackedExpression as a valid lvalue for update expressions
|
|
986
|
-
* @type {Parse.Parser['checkLValSimple']}
|
|
987
|
-
*/
|
|
988
|
-
checkLValSimple(expr, bindingType, checkClashes) {
|
|
989
|
-
// Allow TrackedExpression as a valid lvalue for ++/-- operators
|
|
990
|
-
if (expr.type === 'TrackedExpression') {
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
return super.checkLValSimple(expr, bindingType, checkClashes);
|
|
994
|
-
}
|
|
995
|
-
|
|
996
765
|
/**
|
|
997
766
|
* Override checkLocalExport to check all scopes in the scope stack.
|
|
998
767
|
* This is needed because server blocks create nested scopes, but exports
|
|
@@ -1131,6 +900,7 @@ function RipplePlugin(config) {
|
|
|
1131
900
|
return this.parseFor(node, null);
|
|
1132
901
|
}
|
|
1133
902
|
|
|
903
|
+
// @ts-ignore — acorn internal: isLet accepts 0 args at runtime
|
|
1134
904
|
let isLet = this.isLet();
|
|
1135
905
|
if (this.type === tt._var || this.type === tt._const || isLet) {
|
|
1136
906
|
let init = /** @type {AST.VariableDeclaration} */ (this.startNode()),
|
|
@@ -1173,7 +943,9 @@ function RipplePlugin(config) {
|
|
|
1173
943
|
}
|
|
1174
944
|
|
|
1175
945
|
let containsEsc = this.containsEsc;
|
|
1176
|
-
let refDestructuringErrors = new DestructuringErrors(
|
|
946
|
+
let refDestructuringErrors = new /** @type {new () => Parse.DestructuringErrors} */ (
|
|
947
|
+
/** @type {unknown} */ (DestructuringErrors)
|
|
948
|
+
)();
|
|
1177
949
|
let initPos = this.start;
|
|
1178
950
|
let init_expr =
|
|
1179
951
|
awaitAt > -1
|
|
@@ -1342,7 +1114,6 @@ function RipplePlugin(config) {
|
|
|
1342
1114
|
jsx_parseExpressionContainer() {
|
|
1343
1115
|
let node = /** @type {ESTreeJSX.JSXExpressionContainer} */ (this.startNode());
|
|
1344
1116
|
this.next();
|
|
1345
|
-
let tracked = false;
|
|
1346
1117
|
|
|
1347
1118
|
if (this.value === 'html') {
|
|
1348
1119
|
node.html = true;
|
|
@@ -1353,20 +1124,12 @@ function RipplePlugin(config) {
|
|
|
1353
1124
|
'"html" is a Ripple keyword and must be used in the form {html some_content}',
|
|
1354
1125
|
);
|
|
1355
1126
|
}
|
|
1356
|
-
if (this.type.label === '@') {
|
|
1357
|
-
this.next(); // consume @
|
|
1358
|
-
tracked = true;
|
|
1359
|
-
}
|
|
1360
1127
|
}
|
|
1361
1128
|
|
|
1362
1129
|
node.expression =
|
|
1363
1130
|
this.type === tt.braceR ? this.jsx_parseEmptyExpression() : this.parseExpression();
|
|
1364
1131
|
this.expect(tt.braceR);
|
|
1365
1132
|
|
|
1366
|
-
if (tracked && node.expression.type === 'Identifier') {
|
|
1367
|
-
node.expression.tracked = true;
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
1133
|
return this.finishNode(node, 'JSXExpressionContainer');
|
|
1371
1134
|
}
|
|
1372
1135
|
|
|
@@ -1429,10 +1192,6 @@ function RipplePlugin(config) {
|
|
|
1429
1192
|
} else {
|
|
1430
1193
|
const id = /** @type {AST.Identifier} */ (this.parseIdentNode());
|
|
1431
1194
|
id.tracked = false;
|
|
1432
|
-
if (id.name.startsWith('@')) {
|
|
1433
|
-
set_tracked_name(id, id.name);
|
|
1434
|
-
id.tracked = true;
|
|
1435
|
-
}
|
|
1436
1195
|
this.finishNode(id, 'Identifier');
|
|
1437
1196
|
/** @type {AST.Attribute} */ (node).name = id;
|
|
1438
1197
|
/** @type {AST.Attribute} */ (node).value = id;
|
|
@@ -1484,14 +1243,6 @@ function RipplePlugin(config) {
|
|
|
1484
1243
|
// Unexpected token after @
|
|
1485
1244
|
this.unexpected();
|
|
1486
1245
|
}
|
|
1487
|
-
} else if (
|
|
1488
|
-
(this.type === tt.name || this.type === tstt.jsxName) &&
|
|
1489
|
-
this.value &&
|
|
1490
|
-
/** @type {string} */ (this.value).startsWith('@')
|
|
1491
|
-
) {
|
|
1492
|
-
set_tracked_name(node, /** @type {string} */ (this.value));
|
|
1493
|
-
node.tracked = true;
|
|
1494
|
-
this.next();
|
|
1495
1246
|
} else if (this.type === tt.name || this.type.keyword || this.type === tstt.jsxName) {
|
|
1496
1247
|
node.name = /** @type {string} */ (this.value);
|
|
1497
1248
|
node.tracked = false; // Explicitly mark as not tracked
|
|
@@ -1525,40 +1276,9 @@ function RipplePlugin(config) {
|
|
|
1525
1276
|
)
|
|
1526
1277
|
);
|
|
1527
1278
|
memberExpr.object = node;
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
if (this.type.label === '@') {
|
|
1532
|
-
// Check if the next character after @ is [
|
|
1533
|
-
const nextChar = this.input.charCodeAt(this.pos);
|
|
1534
|
-
|
|
1535
|
-
if (nextChar === 91) {
|
|
1536
|
-
// [ character
|
|
1537
|
-
memberExpr.computed = true;
|
|
1538
|
-
|
|
1539
|
-
// Consume the @ token
|
|
1540
|
-
this.next();
|
|
1541
|
-
|
|
1542
|
-
// Now this.type should be bracketL
|
|
1543
|
-
// Consume the [ and parse the expression inside
|
|
1544
|
-
this.expect(tt.bracketL);
|
|
1545
|
-
|
|
1546
|
-
// Parse the expression inside brackets
|
|
1547
|
-
memberExpr.property = /** @type {ESTreeJSX.JSXIdentifier} */ (this.parseExpression());
|
|
1548
|
-
/** @type {AST.TrackedNode} */ (memberExpr.property).tracked = true;
|
|
1549
|
-
|
|
1550
|
-
// Expect closing bracket
|
|
1551
|
-
this.expect(tt.bracketR);
|
|
1552
|
-
} else {
|
|
1553
|
-
// @ not followed by [, treat as regular tracked identifier
|
|
1554
|
-
memberExpr.property = this.jsx_parseIdentifier();
|
|
1555
|
-
memberExpr.computed = false;
|
|
1556
|
-
}
|
|
1557
|
-
} else {
|
|
1558
|
-
// Regular dot notation
|
|
1559
|
-
memberExpr.property = this.jsx_parseIdentifier();
|
|
1560
|
-
memberExpr.computed = false;
|
|
1561
|
-
}
|
|
1279
|
+
memberExpr.property = this.jsx_parseIdentifier();
|
|
1280
|
+
memberExpr.computed = false;
|
|
1281
|
+
memberExpr = this.finishNode(memberExpr, 'JSXMemberExpression');
|
|
1562
1282
|
while (this.eat(tt.dot)) {
|
|
1563
1283
|
let newMemberExpr = /** @type {ESTreeJSX.JSXMemberExpression} */ (
|
|
1564
1284
|
this.startNodeAt(
|
|
@@ -1571,7 +1291,7 @@ function RipplePlugin(config) {
|
|
|
1571
1291
|
newMemberExpr.computed = false;
|
|
1572
1292
|
memberExpr = this.finishNode(newMemberExpr, 'JSXMemberExpression');
|
|
1573
1293
|
}
|
|
1574
|
-
return
|
|
1294
|
+
return memberExpr;
|
|
1575
1295
|
}
|
|
1576
1296
|
return node;
|
|
1577
1297
|
}
|
|
@@ -1842,8 +1562,9 @@ function RipplePlugin(config) {
|
|
|
1842
1562
|
if (expression.type === 'Literal') {
|
|
1843
1563
|
expression.was_expression = true;
|
|
1844
1564
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
1565
|
+
// @ts-ignore — intentional AST node conversion from JSX to Ripple
|
|
1566
|
+
/** @type {ESTreeJSX.JSXAttribute} */ (attr).value =
|
|
1567
|
+
/** @type {ESTreeJSX.JSXExpressionContainer['expression']} */ (expression);
|
|
1847
1568
|
}
|
|
1848
1569
|
}
|
|
1849
1570
|
}
|
|
@@ -2355,53 +2076,6 @@ function RipplePlugin(config) {
|
|
|
2355
2076
|
this.awaitPos = 0;
|
|
2356
2077
|
return this.parseComponent({ requireName: true, declareName: true });
|
|
2357
2078
|
}
|
|
2358
|
-
if (this.type.label === '@') {
|
|
2359
|
-
// Try to parse as an expression statement first using tryParse
|
|
2360
|
-
// This allows us to handle Ripple @ syntax like @count++ without
|
|
2361
|
-
// interfering with legitimate decorator syntax
|
|
2362
|
-
this.skip_decorator = true;
|
|
2363
|
-
const expressionResult = this.tryParse(() => {
|
|
2364
|
-
const node = /** @type {AST.ExpressionStatement} */ (this.startNode());
|
|
2365
|
-
this.next();
|
|
2366
|
-
// Force expression context to ensure @ is tokenized correctly
|
|
2367
|
-
const old_expr_allowed = this.exprAllowed;
|
|
2368
|
-
this.exprAllowed = true;
|
|
2369
|
-
node.expression = this.parseExpression();
|
|
2370
|
-
|
|
2371
|
-
if (node.expression.type === 'UpdateExpression') {
|
|
2372
|
-
/** @type {AST.Expression} */
|
|
2373
|
-
let object = node.expression.argument;
|
|
2374
|
-
while (object.type === 'MemberExpression') {
|
|
2375
|
-
object = /** @type {AST.Expression} */ (object.object);
|
|
2376
|
-
}
|
|
2377
|
-
if (object.type === 'Identifier') {
|
|
2378
|
-
object.tracked = true;
|
|
2379
|
-
}
|
|
2380
|
-
} else if (node.expression.type === 'AssignmentExpression') {
|
|
2381
|
-
/** @type {AST.Expression | AST.Pattern | AST.Identifier} */
|
|
2382
|
-
let object = node.expression.left;
|
|
2383
|
-
while (object.type === 'MemberExpression') {
|
|
2384
|
-
object = /** @type {AST.Expression} */ (object.object);
|
|
2385
|
-
}
|
|
2386
|
-
if (object.type === 'Identifier') {
|
|
2387
|
-
object.tracked = true;
|
|
2388
|
-
}
|
|
2389
|
-
} else if (node.expression.type === 'Identifier') {
|
|
2390
|
-
node.expression.tracked = true;
|
|
2391
|
-
} else {
|
|
2392
|
-
// TODO?
|
|
2393
|
-
}
|
|
2394
|
-
|
|
2395
|
-
this.exprAllowed = old_expr_allowed;
|
|
2396
|
-
return this.finishNode(node, 'ExpressionStatement');
|
|
2397
|
-
});
|
|
2398
|
-
this.skip_decorator = false;
|
|
2399
|
-
|
|
2400
|
-
// If parsing as expression statement succeeded, use that result
|
|
2401
|
-
if (expressionResult.node) {
|
|
2402
|
-
return expressionResult.node;
|
|
2403
|
-
}
|
|
2404
|
-
}
|
|
2405
2079
|
|
|
2406
2080
|
if (this.type === tstt.jsxTagStart) {
|
|
2407
2081
|
this.next();
|
|
@@ -2416,6 +2090,37 @@ function RipplePlugin(config) {
|
|
|
2416
2090
|
return node;
|
|
2417
2091
|
}
|
|
2418
2092
|
|
|
2093
|
+
// &[ or &{ at statement level — lazy destructuring assignment
|
|
2094
|
+
// e.g., &[data] = track(0); or &{x, y} = obj;
|
|
2095
|
+
if (this.type === tt.bitwiseAND) {
|
|
2096
|
+
const charAfterAmp = this.input.charCodeAt(this.end);
|
|
2097
|
+
if (charAfterAmp === 123 || charAfterAmp === 91) {
|
|
2098
|
+
const node = /** @type {AST.ExpressionStatement} */ (this.startNode());
|
|
2099
|
+
const assign_node = /** @type {AST.AssignmentExpression} */ (this.startNode());
|
|
2100
|
+
this.next(); // consume &
|
|
2101
|
+
// Parse the left-hand side (array or object expression)
|
|
2102
|
+
const left = /** @type {AST.ArrayPattern | AST.ObjectPattern} */ (
|
|
2103
|
+
/** @type {unknown} */ (this.parseExprAtom())
|
|
2104
|
+
);
|
|
2105
|
+
// Convert expression to destructuring pattern
|
|
2106
|
+
this.toAssignable(left, false);
|
|
2107
|
+
left.lazy = true;
|
|
2108
|
+
// Expect = operator
|
|
2109
|
+
this.expect(tt.eq);
|
|
2110
|
+
// Parse the right-hand side
|
|
2111
|
+
assign_node.operator = '=';
|
|
2112
|
+
assign_node.left = left;
|
|
2113
|
+
assign_node.right = /** @type {AST.Expression} */ (this.parseMaybeAssign());
|
|
2114
|
+
node.expression = /** @type {AST.AssignmentExpression} */ (
|
|
2115
|
+
this.finishNode(assign_node, 'AssignmentExpression')
|
|
2116
|
+
);
|
|
2117
|
+
this.semicolon();
|
|
2118
|
+
return /** @type {AST.ExpressionStatement} */ (
|
|
2119
|
+
this.finishNode(node, 'ExpressionStatement')
|
|
2120
|
+
);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2419
2124
|
return super.parseStatement(context, topLevel, exports);
|
|
2420
2125
|
}
|
|
2421
2126
|
|
|
@@ -2809,15 +2514,9 @@ function get_comment_handlers(source, comments, index = 0) {
|
|
|
2809
2514
|
isSwitchCaseSibling = true;
|
|
2810
2515
|
} else if (parent.type === 'SwitchCase') {
|
|
2811
2516
|
node_array = parent.consequent;
|
|
2812
|
-
} else if (
|
|
2813
|
-
parent.type === 'ArrayExpression' ||
|
|
2814
|
-
parent.type === 'RippleArrayExpression'
|
|
2815
|
-
) {
|
|
2517
|
+
} else if (parent.type === 'ArrayExpression') {
|
|
2816
2518
|
node_array = parent.elements;
|
|
2817
|
-
} else if (
|
|
2818
|
-
parent.type === 'ObjectExpression' ||
|
|
2819
|
-
parent.type === 'RippleObjectExpression'
|
|
2820
|
-
) {
|
|
2519
|
+
} else if (parent.type === 'ObjectExpression') {
|
|
2821
2520
|
node_array = parent.properties;
|
|
2822
2521
|
} else if (
|
|
2823
2522
|
parent.type === 'FunctionDeclaration' ||
|