ripple 0.3.9 → 0.3.11
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 +43 -0
- package/package.json +2 -2
- package/src/compiler/errors.js +1 -1
- package/src/compiler/index.d.ts +3 -1
- package/src/compiler/phases/1-parse/index.js +195 -23
- package/src/compiler/phases/2-analyze/index.js +266 -108
- package/src/compiler/phases/2-analyze/prune.js +13 -5
- package/src/compiler/phases/3-transform/client/index.js +304 -80
- package/src/compiler/phases/3-transform/server/index.js +108 -43
- package/src/compiler/types/index.d.ts +28 -3
- package/src/compiler/types/parse.d.ts +3 -1
- package/src/compiler/utils.js +275 -1
- package/src/runtime/element.js +39 -0
- package/src/runtime/index-client.js +14 -4
- package/src/runtime/internal/client/composite.js +10 -6
- package/src/runtime/internal/client/expression.js +280 -0
- package/src/runtime/internal/client/index.js +4 -0
- package/src/runtime/internal/client/portal.js +12 -6
- package/src/runtime/internal/server/index.js +26 -1
- package/src/utils/builders.js +30 -0
- package/tests/client/basic/__snapshots__/basic.rendering.test.ripple.snap +1 -0
- package/tests/client/basic/basic.components.test.ripple +85 -87
- package/tests/client/basic/basic.errors.test.ripple +4 -8
- package/tests/client/basic/basic.rendering.test.ripple +27 -10
- package/tests/client/capture-error.js +12 -0
- package/tests/client/compiler/compiler.basic.test.ripple +76 -6
- package/tests/client/composite/composite.props.test.ripple +1 -3
- package/tests/client/composite/composite.render.test.ripple +91 -13
- package/tests/client/css/global-additional-cases.test.ripple +3 -3
- package/tests/client/return.test.ripple +101 -0
- package/tests/client/svg.test.ripple +4 -4
- package/tests/client/tsx.test.ripple +486 -0
- package/tests/hydration/basic.test.js +23 -0
- package/tests/hydration/compiled/client/basic.js +111 -75
- package/tests/hydration/compiled/client/composite.js +81 -46
- package/tests/hydration/compiled/client/events.js +18 -63
- package/tests/hydration/compiled/client/for.js +90 -183
- package/tests/hydration/compiled/client/head.js +10 -25
- package/tests/hydration/compiled/client/hmr.js +10 -13
- package/tests/hydration/compiled/client/html.js +251 -380
- package/tests/hydration/compiled/client/if-children.js +35 -45
- package/tests/hydration/compiled/client/if.js +2 -2
- package/tests/hydration/compiled/client/mixed-control-flow.js +24 -72
- package/tests/hydration/compiled/client/nested-control-flow.js +115 -391
- package/tests/hydration/compiled/client/portal.js +8 -20
- package/tests/hydration/compiled/client/reactivity.js +14 -47
- package/tests/hydration/compiled/client/return.js +2 -5
- package/tests/hydration/compiled/client/try.js +4 -4
- package/tests/hydration/compiled/server/basic.js +64 -31
- package/tests/hydration/compiled/server/composite.js +62 -29
- package/tests/hydration/compiled/server/hmr.js +24 -37
- package/tests/hydration/compiled/server/html.js +472 -611
- package/tests/hydration/compiled/server/if-children.js +77 -103
- package/tests/hydration/compiled/server/portal.js +8 -8
- package/tests/hydration/components/basic.ripple +15 -5
- package/tests/hydration/components/composite.ripple +13 -1
- package/tests/hydration/components/hmr.ripple +1 -3
- package/tests/hydration/components/html.ripple +13 -35
- package/tests/hydration/components/if-children.ripple +4 -8
- package/tests/hydration/composite.test.js +11 -0
- package/tests/server/basic.attributes.test.ripple +50 -0
- package/tests/server/basic.components.test.ripple +22 -28
- package/tests/server/basic.test.ripple +12 -0
- package/tests/server/compiler.test.ripple +25 -8
- package/tests/server/composite.props.test.ripple +1 -3
- package/tests/server/style-identifier.test.ripple +2 -4
- package/tests/utils/compiler-compat-config.test.js +38 -0
- package/tests/utils/vite-plugin-config.test.js +113 -0
- package/tsconfig.typecheck.json +2 -1
- package/types/index.d.ts +8 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# ripple
|
|
2
2
|
|
|
3
|
+
## 0.3.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#853](https://github.com/Ripple-TS/ripple/pull/853)
|
|
8
|
+
[`6792c70`](https://github.com/Ripple-TS/ripple/commit/6792c700db30ec0c25077bf8892753f18eddc5cc)
|
|
9
|
+
Thanks [@RazinShafayet2007](https://github.com/RazinShafayet2007)! -
|
|
10
|
+
fix(compiler): add `throw` statement support in `if` blocks
|
|
11
|
+
|
|
12
|
+
- [#858](https://github.com/Ripple-TS/ripple/pull/858)
|
|
13
|
+
[`f2624a6`](https://github.com/Ripple-TS/ripple/commit/f2624a6596479480c47317ea3030863214a6e2b3)
|
|
14
|
+
Thanks [@RazinShafayet2007](https://github.com/RazinShafayet2007)! - fix: scoped
|
|
15
|
+
styles apply correctly when child content is rendered through a parent component
|
|
16
|
+
|
|
17
|
+
- [#840](https://github.com/Ripple-TS/ripple/pull/840)
|
|
18
|
+
[`13323dd`](https://github.com/Ripple-TS/ripple/commit/13323dddbcb68e1e8e373142884a7c54fbb76cd7)
|
|
19
|
+
Thanks [@trueadm](https://github.com/trueadm)! - Remove the `compat` option from
|
|
20
|
+
`mount()` and `hydrate()`, and stop exporting the old public compat types from
|
|
21
|
+
`ripple`. Compat integrations are now expected to be provided by the Vite plugin
|
|
22
|
+
via `ripple.config.ts`, while direct runtime tests can seed the generated global
|
|
23
|
+
compat registry.
|
|
24
|
+
|
|
25
|
+
Also add the `reactCompat()` config-facing helper from `@ripple-ts/compat-react`
|
|
26
|
+
for use in `ripple.config.ts`.
|
|
27
|
+
|
|
28
|
+
- Updated dependencies
|
|
29
|
+
[[`6792c70`](https://github.com/Ripple-TS/ripple/commit/6792c700db30ec0c25077bf8892753f18eddc5cc),
|
|
30
|
+
[`f2624a6`](https://github.com/Ripple-TS/ripple/commit/f2624a6596479480c47317ea3030863214a6e2b3),
|
|
31
|
+
[`13323dd`](https://github.com/Ripple-TS/ripple/commit/13323dddbcb68e1e8e373142884a7c54fbb76cd7)]:
|
|
32
|
+
- ripple@0.3.11
|
|
33
|
+
|
|
34
|
+
## 0.3.10
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- [`aef1253`](https://github.com/Ripple-TS/ripple/commit/aef1253dd79c067a8358172d502dc21d8a9a9085)
|
|
39
|
+
Thanks [@trueadm](https://github.com/trueadm)! - Replace `<children />` with
|
|
40
|
+
`{children}` expression syntax for rendering component children
|
|
41
|
+
|
|
42
|
+
- Updated dependencies
|
|
43
|
+
[[`aef1253`](https://github.com/Ripple-TS/ripple/commit/aef1253dd79c067a8358172d502dc21d8a9a9085)]:
|
|
44
|
+
- ripple@0.3.10
|
|
45
|
+
|
|
3
46
|
## 0.3.9
|
|
4
47
|
|
|
5
48
|
### 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.
|
|
6
|
+
"version": "0.3.11",
|
|
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.11"
|
|
109
109
|
}
|
|
110
110
|
}
|
package/src/compiler/errors.js
CHANGED
package/src/compiler/index.d.ts
CHANGED
|
@@ -98,6 +98,7 @@ interface SharedCompileOptions {
|
|
|
98
98
|
export interface CompileOptions extends SharedCompileOptions {
|
|
99
99
|
mode?: 'client' | 'server';
|
|
100
100
|
hmr?: boolean;
|
|
101
|
+
compat_kinds?: string[];
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
export interface ParseOptions {
|
|
@@ -106,7 +107,8 @@ export interface ParseOptions {
|
|
|
106
107
|
comments?: AST.CommentWithLocation[];
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
export interface AnalyzeOptions
|
|
110
|
+
export interface AnalyzeOptions
|
|
111
|
+
extends ParseOptions, Pick<CompileOptions, 'mode' | 'compat_kinds'> {
|
|
110
112
|
errors?: RippleCompileError[];
|
|
111
113
|
to_ts?: boolean;
|
|
112
114
|
}
|
|
@@ -1115,7 +1115,7 @@ function RipplePlugin(config) {
|
|
|
1115
1115
|
let node = /** @type {ESTreeJSX.JSXExpressionContainer} */ (this.startNode());
|
|
1116
1116
|
this.next();
|
|
1117
1117
|
|
|
1118
|
-
if (this.value === 'html') {
|
|
1118
|
+
if (this.type === tt.name && this.value === 'html') {
|
|
1119
1119
|
node.html = true;
|
|
1120
1120
|
this.next();
|
|
1121
1121
|
if (this.type === tt.braceR) {
|
|
@@ -1124,6 +1124,15 @@ function RipplePlugin(config) {
|
|
|
1124
1124
|
'"html" is a Ripple keyword and must be used in the form {html some_content}',
|
|
1125
1125
|
);
|
|
1126
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
|
+
}
|
|
1127
1136
|
}
|
|
1128
1137
|
|
|
1129
1138
|
node.expression =
|
|
@@ -1328,16 +1337,12 @@ function RipplePlugin(config) {
|
|
|
1328
1337
|
const clause = /** @type {AST.CatchClause} */ (this.startNode());
|
|
1329
1338
|
this.next();
|
|
1330
1339
|
if (this.eat(tt.parenL)) {
|
|
1331
|
-
clause.param = this.
|
|
1340
|
+
clause.param = this.parseBindingAtom();
|
|
1341
|
+
this.expect(tt.parenR);
|
|
1332
1342
|
} else {
|
|
1333
|
-
if (this.options.ecmaVersion < 10) {
|
|
1334
|
-
this.unexpected();
|
|
1335
|
-
}
|
|
1336
1343
|
clause.param = null;
|
|
1337
|
-
this.enterScope(0);
|
|
1338
1344
|
}
|
|
1339
|
-
clause.body = this.parseBlock(
|
|
1340
|
-
this.exitScope();
|
|
1345
|
+
clause.body = this.parseBlock();
|
|
1341
1346
|
node.handler = this.finishNode(clause, 'CatchClause');
|
|
1342
1347
|
}
|
|
1343
1348
|
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null;
|
|
@@ -1353,7 +1358,9 @@ function RipplePlugin(config) {
|
|
|
1353
1358
|
|
|
1354
1359
|
/** @type {Parse.Parser['jsx_readToken']} */
|
|
1355
1360
|
jsx_readToken() {
|
|
1356
|
-
const inside_tsx_compat = this.#path.findLast(
|
|
1361
|
+
const inside_tsx_compat = this.#path.findLast(
|
|
1362
|
+
(n) => n.type === 'TsxCompat' || n.type === 'Tsx',
|
|
1363
|
+
);
|
|
1357
1364
|
if (inside_tsx_compat) {
|
|
1358
1365
|
return super.jsx_readToken();
|
|
1359
1366
|
}
|
|
@@ -1504,6 +1511,48 @@ function RipplePlugin(config) {
|
|
|
1504
1511
|
}
|
|
1505
1512
|
}
|
|
1506
1513
|
|
|
1514
|
+
/**
|
|
1515
|
+
* Override jsx_parseElement to intercept expression-level JSX.
|
|
1516
|
+
* This is called by acorn-jsx's parseExprAtom when it encounters <
|
|
1517
|
+
* in expression position. Only <tsx> and <tsx:*> are allowed.
|
|
1518
|
+
* @type {Parse.Parser['jsx_parseElement']}
|
|
1519
|
+
*/
|
|
1520
|
+
jsx_parseElement() {
|
|
1521
|
+
const inside_tsx = this.#path.findLast((n) => n.type === 'TsxCompat' || n.type === 'Tsx');
|
|
1522
|
+
if (inside_tsx) {
|
|
1523
|
+
// Inside tsx/tsx:*, let acorn-jsx handle it normally
|
|
1524
|
+
return super.jsx_parseElement();
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
// Check if the element being parsed IS a <tsx> or <tsx:*> tag
|
|
1528
|
+
// Current token is jsxTagStart, this.end is position after '<'
|
|
1529
|
+
const tag_name_start = this.end;
|
|
1530
|
+
const char_after_tsx = this.input.charCodeAt(tag_name_start + 3);
|
|
1531
|
+
const is_tsx_tag =
|
|
1532
|
+
this.input.startsWith('tsx', tag_name_start) &&
|
|
1533
|
+
(tag_name_start + 3 >= this.input.length ||
|
|
1534
|
+
char_after_tsx === 62 || // >
|
|
1535
|
+
char_after_tsx === 47 || // / (self-closing)
|
|
1536
|
+
char_after_tsx === 32 || // space
|
|
1537
|
+
char_after_tsx === 9 || // tab
|
|
1538
|
+
char_after_tsx === 10 || // newline
|
|
1539
|
+
char_after_tsx === 13 || // carriage return
|
|
1540
|
+
char_after_tsx === 58); // : (tsx:react)
|
|
1541
|
+
|
|
1542
|
+
if (is_tsx_tag) {
|
|
1543
|
+
// Use Ripple's parseElement to create a Tsx/TsxCompat node
|
|
1544
|
+
this.next();
|
|
1545
|
+
return /** @type {import('estree-jsx').JSXElement} */ (
|
|
1546
|
+
/** @type {unknown} */ (this.parseElement())
|
|
1547
|
+
);
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
this.raise(
|
|
1551
|
+
this.start,
|
|
1552
|
+
'JSX elements cannot be used as expressions. Wrap with `<tsx>...</tsx>` or use elements as statements within a component.',
|
|
1553
|
+
);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1507
1556
|
/**
|
|
1508
1557
|
* @type {Parse.Parser['parseElement']}
|
|
1509
1558
|
*/
|
|
@@ -1515,7 +1564,7 @@ function RipplePlugin(config) {
|
|
|
1515
1564
|
const start = this.start - 1;
|
|
1516
1565
|
const position = new acorn.Position(this.curLine, start - this.lineStart);
|
|
1517
1566
|
|
|
1518
|
-
const element = /** @type {AST.Element | AST.TsxCompat} */ (this.startNode());
|
|
1567
|
+
const element = /** @type {AST.Element | AST.Tsx | AST.TsxCompat} */ (this.startNode());
|
|
1519
1568
|
element.start = start;
|
|
1520
1569
|
/** @type {AST.NodeWithLocation} */ (element).loc.start = position;
|
|
1521
1570
|
element.metadata = { path: [] };
|
|
@@ -1530,6 +1579,8 @@ function RipplePlugin(config) {
|
|
|
1530
1579
|
|
|
1531
1580
|
// Check if this is a namespaced element (tsx:react)
|
|
1532
1581
|
const is_tsx_compat = open.name.type === 'JSXNamespacedName';
|
|
1582
|
+
const is_tsx =
|
|
1583
|
+
!is_tsx_compat && open.name.type === 'JSXIdentifier' && open.name.name === 'tsx';
|
|
1533
1584
|
|
|
1534
1585
|
if (is_tsx_compat) {
|
|
1535
1586
|
const namespace_node = /** @type {ESTreeJSX.JSXNamespacedName} */ (open.name);
|
|
@@ -1543,6 +1594,15 @@ function RipplePlugin(config) {
|
|
|
1543
1594
|
`TSX compatibility elements cannot be self-closing. '<${tagName} />' must have a closing tag '</${tagName}>'.`,
|
|
1544
1595
|
);
|
|
1545
1596
|
}
|
|
1597
|
+
} else if (is_tsx) {
|
|
1598
|
+
/** @type {AST.Tsx} */ (element).type = 'Tsx';
|
|
1599
|
+
|
|
1600
|
+
if (open.selfClosing) {
|
|
1601
|
+
this.raise(
|
|
1602
|
+
open.start,
|
|
1603
|
+
`TSX elements cannot be self-closing. '<tsx />' must have a closing tag '</tsx>'.`,
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1546
1606
|
} else {
|
|
1547
1607
|
element.type = 'Element';
|
|
1548
1608
|
}
|
|
@@ -1570,7 +1630,7 @@ function RipplePlugin(config) {
|
|
|
1570
1630
|
}
|
|
1571
1631
|
}
|
|
1572
1632
|
|
|
1573
|
-
if (!is_tsx_compat) {
|
|
1633
|
+
if (!is_tsx_compat && !is_tsx) {
|
|
1574
1634
|
/** @type {AST.Element} */ (element).id = /** @type {AST.Identifier} */ (
|
|
1575
1635
|
convert_from_jsx(/** @type {ESTreeJSX.JSXIdentifier} */ (open.name))
|
|
1576
1636
|
);
|
|
@@ -1740,6 +1800,7 @@ function RipplePlugin(config) {
|
|
|
1740
1800
|
const insideTemplate =
|
|
1741
1801
|
parent?.type === 'Component' ||
|
|
1742
1802
|
parent?.type === 'Element' ||
|
|
1803
|
+
parent?.type === 'Tsx' ||
|
|
1743
1804
|
parent?.type === 'TsxCompat';
|
|
1744
1805
|
|
|
1745
1806
|
if (curContext === tstc.tc_expr && !insideTemplate) {
|
|
@@ -1752,7 +1813,30 @@ function RipplePlugin(config) {
|
|
|
1752
1813
|
this.parseTemplateBody(/** @type {AST.Element} */ (element).children);
|
|
1753
1814
|
this.exitScope();
|
|
1754
1815
|
|
|
1755
|
-
if (element.type === '
|
|
1816
|
+
if (element.type === 'Tsx') {
|
|
1817
|
+
this.#path.pop();
|
|
1818
|
+
|
|
1819
|
+
if (!element.unclosed) {
|
|
1820
|
+
const raise_error = () => {
|
|
1821
|
+
this.raise(this.start, `Expected closing tag '</tsx>'`);
|
|
1822
|
+
};
|
|
1823
|
+
|
|
1824
|
+
this.next();
|
|
1825
|
+
// we should expect to see </tsx>
|
|
1826
|
+
if (this.value !== '/') {
|
|
1827
|
+
raise_error();
|
|
1828
|
+
}
|
|
1829
|
+
this.next();
|
|
1830
|
+
if (this.value !== 'tsx') {
|
|
1831
|
+
raise_error();
|
|
1832
|
+
}
|
|
1833
|
+
this.next();
|
|
1834
|
+
if (this.type !== tstt.jsxTagEnd) {
|
|
1835
|
+
raise_error();
|
|
1836
|
+
}
|
|
1837
|
+
this.next();
|
|
1838
|
+
}
|
|
1839
|
+
} else if (element.type === 'TsxCompat') {
|
|
1756
1840
|
this.#path.pop();
|
|
1757
1841
|
|
|
1758
1842
|
if (!element.unclosed) {
|
|
@@ -1808,6 +1892,7 @@ function RipplePlugin(config) {
|
|
|
1808
1892
|
const insideTemplate =
|
|
1809
1893
|
parent?.type === 'Component' ||
|
|
1810
1894
|
parent?.type === 'Element' ||
|
|
1895
|
+
parent?.type === 'Tsx' ||
|
|
1811
1896
|
parent?.type === 'TsxCompat';
|
|
1812
1897
|
|
|
1813
1898
|
if (curContext === tstc.tc_expr && !insideTemplate) {
|
|
@@ -1815,7 +1900,7 @@ function RipplePlugin(config) {
|
|
|
1815
1900
|
}
|
|
1816
1901
|
}
|
|
1817
1902
|
|
|
1818
|
-
if (element.closingElement && !is_tsx_compat) {
|
|
1903
|
+
if (element.closingElement && !is_tsx_compat && !is_tsx) {
|
|
1819
1904
|
/** @type {unknown} */ (element.closingElement.name) = convert_from_jsx(
|
|
1820
1905
|
element.closingElement.name,
|
|
1821
1906
|
);
|
|
@@ -1831,6 +1916,7 @@ function RipplePlugin(config) {
|
|
|
1831
1916
|
parseTemplateBody(body) {
|
|
1832
1917
|
const inside_func =
|
|
1833
1918
|
this.context.some((n) => n.token === 'function') || this.scopeStack.length > 1;
|
|
1919
|
+
const inside_tsx = this.#path.findLast((n) => n.type === 'Tsx');
|
|
1834
1920
|
const inside_tsx_compat = this.#path.findLast((n) => n.type === 'TsxCompat');
|
|
1835
1921
|
|
|
1836
1922
|
if (!inside_func) {
|
|
@@ -1842,6 +1928,75 @@ function RipplePlugin(config) {
|
|
|
1842
1928
|
}
|
|
1843
1929
|
}
|
|
1844
1930
|
|
|
1931
|
+
if (inside_tsx) {
|
|
1932
|
+
this.exprAllowed = true;
|
|
1933
|
+
|
|
1934
|
+
while (true) {
|
|
1935
|
+
if (this.type === tt.eof || this.pos >= this.input.length || this.type === tt.braceR) {
|
|
1936
|
+
if (!this.#loose) {
|
|
1937
|
+
this.raise(
|
|
1938
|
+
this.start,
|
|
1939
|
+
`Unclosed tag '<tsx>'. Expected '</tsx>' before end of component.`,
|
|
1940
|
+
);
|
|
1941
|
+
} else {
|
|
1942
|
+
inside_tsx.unclosed = true;
|
|
1943
|
+
/** @type {AST.NodeWithLocation} */ (inside_tsx).loc.end = {
|
|
1944
|
+
.../** @type {AST.SourceLocation} */ (inside_tsx.openingElement.loc).end,
|
|
1945
|
+
};
|
|
1946
|
+
inside_tsx.end = inside_tsx.openingElement.end;
|
|
1947
|
+
}
|
|
1948
|
+
return;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
if (this.input.slice(this.pos, this.pos + 4) === '/tsx') {
|
|
1952
|
+
const after = this.input.charCodeAt(this.pos + 4);
|
|
1953
|
+
// Make sure it's </tsx> and not </tsx:...>
|
|
1954
|
+
if (after === 62 /* > */) {
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
if (this.type === tt.braceL) {
|
|
1960
|
+
const node = this.jsx_parseExpressionContainer();
|
|
1961
|
+
body.push(node);
|
|
1962
|
+
} else if (this.type === tstt.jsxTagStart) {
|
|
1963
|
+
// Parse JSX element
|
|
1964
|
+
const node = super.parseExpression();
|
|
1965
|
+
body.push(node);
|
|
1966
|
+
} else {
|
|
1967
|
+
const start = this.start;
|
|
1968
|
+
this.pos = start;
|
|
1969
|
+
let text = '';
|
|
1970
|
+
|
|
1971
|
+
while (this.pos < this.input.length) {
|
|
1972
|
+
const ch = this.input.charCodeAt(this.pos);
|
|
1973
|
+
|
|
1974
|
+
// Stop at opening tag, expression, or the component-closing brace
|
|
1975
|
+
if (ch === 60 || ch === 123 || ch === 125) {
|
|
1976
|
+
// < or { or }
|
|
1977
|
+
break;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
text += this.input[this.pos];
|
|
1981
|
+
this.pos++;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
if (text) {
|
|
1985
|
+
const node = /** @type {ESTreeJSX.JSXText} */ ({
|
|
1986
|
+
type: 'JSXText',
|
|
1987
|
+
value: text,
|
|
1988
|
+
raw: text,
|
|
1989
|
+
start,
|
|
1990
|
+
end: this.pos,
|
|
1991
|
+
});
|
|
1992
|
+
body.push(node);
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
// Always call next() to ensure parser makes progress
|
|
1996
|
+
this.next();
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
1845
2000
|
if (inside_tsx_compat) {
|
|
1846
2001
|
this.exprAllowed = true;
|
|
1847
2002
|
|
|
@@ -1909,12 +2064,13 @@ function RipplePlugin(config) {
|
|
|
1909
2064
|
if (this.type === tt.braceL) {
|
|
1910
2065
|
const node = this.jsx_parseExpressionContainer();
|
|
1911
2066
|
// Keep JSXEmptyExpression as-is (for prettier to handle comments)
|
|
1912
|
-
// but convert other expressions to Text
|
|
2067
|
+
// but convert other expressions to Html/RippleExpression/Text nodes
|
|
1913
2068
|
if (node.expression.type !== 'JSXEmptyExpression') {
|
|
1914
|
-
/** @type {AST.Html | AST.TextNode} */ (
|
|
1915
|
-
|
|
1916
|
-
|
|
2069
|
+
/** @type {AST.RippleExpression | AST.Html | AST.TextNode} */ (
|
|
2070
|
+
/** @type {unknown} */ (node)
|
|
2071
|
+
).type = node.html ? 'Html' : node.text ? 'Text' : 'RippleExpression';
|
|
1917
2072
|
delete node.html;
|
|
2073
|
+
delete node.text;
|
|
1918
2074
|
}
|
|
1919
2075
|
body.push(node);
|
|
1920
2076
|
} else if (this.type === tt.braceR) {
|
|
@@ -1944,7 +2100,9 @@ function RipplePlugin(config) {
|
|
|
1944
2100
|
const currentElement = this.#path[this.#path.length - 1];
|
|
1945
2101
|
if (
|
|
1946
2102
|
!currentElement ||
|
|
1947
|
-
(currentElement.type !== 'Element' &&
|
|
2103
|
+
(currentElement.type !== 'Element' &&
|
|
2104
|
+
currentElement.type !== 'Tsx' &&
|
|
2105
|
+
currentElement.type !== 'TsxCompat')
|
|
1948
2106
|
) {
|
|
1949
2107
|
this.raise(this.start, 'Unexpected closing tag');
|
|
1950
2108
|
}
|
|
@@ -1960,6 +2118,12 @@ function RipplePlugin(config) {
|
|
|
1960
2118
|
closingElement.name?.type === 'JSXNamespacedName'
|
|
1961
2119
|
? closingElement.name.namespace.name + ':' + closingElement.name.name.name
|
|
1962
2120
|
: this.getElementName(closingElement.name);
|
|
2121
|
+
} else if (currentElement.type === 'Tsx') {
|
|
2122
|
+
openingTagName = 'tsx';
|
|
2123
|
+
closingTagName =
|
|
2124
|
+
closingElement.name?.type === 'JSXNamespacedName'
|
|
2125
|
+
? closingElement.name.namespace.name + ':' + closingElement.name.name.name
|
|
2126
|
+
: this.getElementName(closingElement.name);
|
|
1963
2127
|
} else {
|
|
1964
2128
|
// Regular Element node
|
|
1965
2129
|
openingTagName = this.getElementName(currentElement.id);
|
|
@@ -1981,12 +2145,16 @@ function RipplePlugin(config) {
|
|
|
1981
2145
|
const elem = this.#path[this.#path.length - 1];
|
|
1982
2146
|
|
|
1983
2147
|
// Stop at non-Element boundaries (Component, etc.)
|
|
1984
|
-
if (elem.type !== 'Element' && elem.type !== 'TsxCompat') {
|
|
2148
|
+
if (elem.type !== 'Element' && elem.type !== 'Tsx' && elem.type !== 'TsxCompat') {
|
|
1985
2149
|
break;
|
|
1986
2150
|
}
|
|
1987
2151
|
|
|
1988
2152
|
const elemName =
|
|
1989
|
-
elem.type === 'TsxCompat'
|
|
2153
|
+
elem.type === 'TsxCompat'
|
|
2154
|
+
? 'tsx:' + elem.kind
|
|
2155
|
+
: elem.type === 'Tsx'
|
|
2156
|
+
? 'tsx'
|
|
2157
|
+
: this.getElementName(elem.id);
|
|
1990
2158
|
|
|
1991
2159
|
// Found matching opening tag
|
|
1992
2160
|
if (elemName === closingTagName) {
|
|
@@ -2050,12 +2218,16 @@ function RipplePlugin(config) {
|
|
|
2050
2218
|
this.context.some((c) => c === tstc.tc_expr)
|
|
2051
2219
|
) {
|
|
2052
2220
|
const node = this.jsx_parseExpressionContainer();
|
|
2053
|
-
// Keep JSXEmptyExpression as-is (don't convert to Text)
|
|
2221
|
+
// Keep JSXEmptyExpression as-is (don't convert to RippleExpression/Text/Html)
|
|
2054
2222
|
if (node.expression.type !== 'JSXEmptyExpression') {
|
|
2055
|
-
/** @type {AST.
|
|
2223
|
+
/** @type {AST.RippleExpression | AST.Html | AST.TextNode} */ (
|
|
2224
|
+
/** @type {unknown} */ (node)
|
|
2225
|
+
).type = node.html ? 'Html' : node.text ? 'Text' : 'RippleExpression';
|
|
2226
|
+
delete node.html;
|
|
2227
|
+
delete node.text;
|
|
2056
2228
|
}
|
|
2057
2229
|
|
|
2058
|
-
return /** @type {ESTreeJSX.JSXEmptyExpression | AST.TextNode | ESTreeJSX.JSXExpressionContainer} */ (
|
|
2230
|
+
return /** @type {ESTreeJSX.JSXEmptyExpression | AST.RippleExpression | AST.Html | AST.TextNode | ESTreeJSX.JSXExpressionContainer} */ (
|
|
2059
2231
|
/** @type {unknown} */ (node)
|
|
2060
2232
|
);
|
|
2061
2233
|
}
|