ripple 0.2.135 → 0.2.137
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/package.json +2 -2
- package/src/compiler/phases/1-parse/index.js +27 -6
- package/src/compiler/phases/3-transform/client/index.js +11 -3
- package/src/compiler/phases/3-transform/segments.js +16 -6
- package/tests/client/compiler/compiler.basic.test.ripple +3 -3
- package/tests/setup-client.js +4 -4
- package/types/index.d.ts +11 -1
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.2.
|
|
6
|
+
"version": "0.2.137",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
|
@@ -81,6 +81,6 @@
|
|
|
81
81
|
"typescript": "^5.9.2"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
|
-
"ripple": "0.2.
|
|
84
|
+
"ripple": "0.2.137"
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -29,6 +29,28 @@ function convert_from_jsx(node) {
|
|
|
29
29
|
return node;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const regex_whitespace_only = /\s/;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Skip whitespace characters without skipping comments.
|
|
36
|
+
* This is needed because Acorn's skipSpace() also skips comments, which breaks
|
|
37
|
+
* parsing in certain contexts. Updates parser position and line tracking.
|
|
38
|
+
* @param {acorn.Parser} parser
|
|
39
|
+
*/
|
|
40
|
+
function skipWhitespace(parser) {
|
|
41
|
+
const originalStart = parser.start;
|
|
42
|
+
while (parser.start < parser.input.length && regex_whitespace_only.test(parser.input[parser.start])) {
|
|
43
|
+
parser.start++;
|
|
44
|
+
}
|
|
45
|
+
// Update line tracking if whitespace was skipped
|
|
46
|
+
if (parser.start !== originalStart) {
|
|
47
|
+
const lineInfo = acorn.getLineInfo(parser.input, parser.start);
|
|
48
|
+
parser.curLine = lineInfo.line;
|
|
49
|
+
parser.lineStart = parser.start - lineInfo.column;
|
|
50
|
+
parser.startLoc = lineInfo;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
32
54
|
function isWhitespaceTextNode(node) {
|
|
33
55
|
if (!node || node.type !== 'Text') {
|
|
34
56
|
return false;
|
|
@@ -710,6 +732,7 @@ function RipplePlugin(config) {
|
|
|
710
732
|
this.exitScope();
|
|
711
733
|
|
|
712
734
|
this.next();
|
|
735
|
+
skipWhitespace(this);
|
|
713
736
|
this.finishNode(node, 'Component');
|
|
714
737
|
this.awaitPos = 0;
|
|
715
738
|
|
|
@@ -1576,6 +1599,7 @@ function RipplePlugin(config) {
|
|
|
1576
1599
|
|
|
1577
1600
|
this.#path.pop();
|
|
1578
1601
|
this.next();
|
|
1602
|
+
skipWhitespace(this);
|
|
1579
1603
|
return;
|
|
1580
1604
|
}
|
|
1581
1605
|
const node = this.parseElement();
|
|
@@ -1588,12 +1612,8 @@ function RipplePlugin(config) {
|
|
|
1588
1612
|
|
|
1589
1613
|
// Ensure we're not in JSX context before recursing
|
|
1590
1614
|
// This is important when elements are parsed at statement level
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
const curContext = this.curContext();
|
|
1594
|
-
if (curContext === tokContexts.tc_expr) {
|
|
1595
|
-
this.context.pop();
|
|
1596
|
-
}
|
|
1615
|
+
if (this.curContext() === this.acornTypeScript.tokContexts.tc_expr) {
|
|
1616
|
+
this.context.pop();
|
|
1597
1617
|
}
|
|
1598
1618
|
}
|
|
1599
1619
|
|
|
@@ -1643,6 +1663,7 @@ function RipplePlugin(config) {
|
|
|
1643
1663
|
this.exitScope();
|
|
1644
1664
|
|
|
1645
1665
|
this.next();
|
|
1666
|
+
skipWhitespace(this);
|
|
1646
1667
|
this.finishNode(node, 'Component');
|
|
1647
1668
|
this.awaitPos = 0;
|
|
1648
1669
|
|
|
@@ -662,7 +662,7 @@ const visitors = {
|
|
|
662
662
|
[b.id('__compat')],
|
|
663
663
|
needs_fragment
|
|
664
664
|
? b.call(
|
|
665
|
-
'__compat.
|
|
665
|
+
'__compat.jsxs',
|
|
666
666
|
b.id('__compat.Fragment'),
|
|
667
667
|
b.object([
|
|
668
668
|
b.prop(
|
|
@@ -1156,11 +1156,15 @@ const visitors = {
|
|
|
1156
1156
|
}),
|
|
1157
1157
|
];
|
|
1158
1158
|
|
|
1159
|
-
|
|
1159
|
+
const func = b.function(
|
|
1160
1160
|
node.id,
|
|
1161
1161
|
node.params.map((param) => context.visit(param, { ...context.state, metadata })),
|
|
1162
1162
|
b.block(body_statements),
|
|
1163
1163
|
);
|
|
1164
|
+
// Mark that this function was originally a component
|
|
1165
|
+
func.metadata = { ...func.metadata, was_component: true };
|
|
1166
|
+
func.loc = node.loc; // Copy source location for Volar mappings
|
|
1167
|
+
return func;
|
|
1164
1168
|
}
|
|
1165
1169
|
|
|
1166
1170
|
let props = b.id('__props');
|
|
@@ -1189,7 +1193,7 @@ const visitors = {
|
|
|
1189
1193
|
context.state.stylesheets.push(node.css);
|
|
1190
1194
|
}
|
|
1191
1195
|
|
|
1192
|
-
|
|
1196
|
+
const func = b.function(
|
|
1193
1197
|
node.id,
|
|
1194
1198
|
node.params.length > 0
|
|
1195
1199
|
? [b.id('__anchor'), props, b.id('__block')]
|
|
@@ -1201,6 +1205,10 @@ const visitors = {
|
|
|
1201
1205
|
: body_statements),
|
|
1202
1206
|
]),
|
|
1203
1207
|
);
|
|
1208
|
+
// Mark that this function was originally a component
|
|
1209
|
+
func.metadata = { ...func.metadata, was_component: true };
|
|
1210
|
+
func.loc = node.loc; // Copy source location for Volar mappings
|
|
1211
|
+
return func;
|
|
1204
1212
|
},
|
|
1205
1213
|
|
|
1206
1214
|
AssignmentExpression(node, context) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { walk } from 'zimmerframe';
|
|
2
2
|
|
|
3
3
|
export const mapping_data = {
|
|
4
4
|
verification: true,
|
|
@@ -354,6 +354,13 @@ export function convert_source_map_to_mappings(ast, source, generated_code, sour
|
|
|
354
354
|
|
|
355
355
|
return;
|
|
356
356
|
} else if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
357
|
+
// Map function/component keywords
|
|
358
|
+
if (node.metadata?.was_component && node.loc) {
|
|
359
|
+
tokens.push({ source: 'component', generated: 'function' });
|
|
360
|
+
} else if (node.loc && node.type === 'FunctionDeclaration') {
|
|
361
|
+
tokens.push('function');
|
|
362
|
+
}
|
|
363
|
+
|
|
357
364
|
// Visit in source order: id, params, body
|
|
358
365
|
if (node.id) {
|
|
359
366
|
visit(node.id);
|
|
@@ -361,6 +368,9 @@ export function convert_source_map_to_mappings(ast, source, generated_code, sour
|
|
|
361
368
|
if (node.params) {
|
|
362
369
|
for (const param of node.params) {
|
|
363
370
|
visit(param);
|
|
371
|
+
if (param.typeAnnotation) {
|
|
372
|
+
visit(param.typeAnnotation);
|
|
373
|
+
}
|
|
364
374
|
}
|
|
365
375
|
}
|
|
366
376
|
if (node.body) {
|
|
@@ -1260,11 +1270,11 @@ export function convert_source_map_to_mappings(ast, source, generated_code, sour
|
|
|
1260
1270
|
sourceOffsets: [0],
|
|
1261
1271
|
generatedOffsets: [0],
|
|
1262
1272
|
lengths: [1],
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1273
|
+
data: {
|
|
1274
|
+
...mapping_data,
|
|
1275
|
+
codeActions: true, // auto-import
|
|
1276
|
+
rename: false, // avoid rename for a “dummy” mapping
|
|
1277
|
+
}
|
|
1268
1278
|
});
|
|
1269
1279
|
}
|
|
1270
1280
|
|
|
@@ -169,7 +169,7 @@ describe('compiler > basics', () => {
|
|
|
169
169
|
|
|
170
170
|
function Wrapper() {
|
|
171
171
|
return {
|
|
172
|
-
unwrap: function() {
|
|
172
|
+
unwrap: function <T>() {
|
|
173
173
|
return null as unknown as T;
|
|
174
174
|
},
|
|
175
175
|
};
|
|
@@ -186,10 +186,10 @@ describe('compiler > basics', () => {
|
|
|
186
186
|
value: T;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
class Box {
|
|
189
|
+
class Box<T> {
|
|
190
190
|
value: T;
|
|
191
191
|
|
|
192
|
-
method(): T {
|
|
192
|
+
method<T>(): T {
|
|
193
193
|
return this.value;
|
|
194
194
|
}
|
|
195
195
|
}
|
package/tests/setup-client.js
CHANGED
|
@@ -2,13 +2,13 @@ import { beforeEach, afterEach } from 'vitest';
|
|
|
2
2
|
import { mount } from 'ripple';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* @param {() => void} component
|
|
5
|
+
* @param {() => void} component
|
|
6
6
|
*/
|
|
7
7
|
globalThis.render = function render(component) {
|
|
8
8
|
mount(component, {
|
|
9
|
-
target: /** @type {HTMLDivElement} */ (globalThis.container)
|
|
9
|
+
target: /** @type {HTMLDivElement} */ (globalThis.container),
|
|
10
10
|
});
|
|
11
|
-
}
|
|
11
|
+
};
|
|
12
12
|
|
|
13
13
|
beforeEach(() => {
|
|
14
14
|
globalThis.container = /** @type {HTMLDivElement} */ (document.createElement('div'));
|
|
@@ -22,7 +22,7 @@ afterEach(() => {
|
|
|
22
22
|
// And when we unset it, we just type-cast it to HTMLDivElement to avoid TS errors, because we
|
|
23
23
|
// know it's guaranteed to exist in the next test again.
|
|
24
24
|
document.body.removeChild(/** @type {HTMLDivElement} */ (globalThis.container));
|
|
25
|
-
globalThis.container = /** @type {HTMLDivElement} */ (/** @type {unknown} */(undefined));
|
|
25
|
+
globalThis.container = /** @type {HTMLDivElement} */ (/** @type {unknown} */ (undefined));
|
|
26
26
|
|
|
27
27
|
globalThis.error = undefined;
|
|
28
28
|
});
|
package/types/index.d.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
export type Component<T = Record<string, any>> = (props: T) => void;
|
|
2
2
|
|
|
3
|
+
export type CompatApi = {
|
|
4
|
+
createRoot: () => void;
|
|
5
|
+
createComponent: (node: any, children_fn: () => any) => void;
|
|
6
|
+
jsx: (type: any, props: any) => any;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type CompatOptions = {
|
|
10
|
+
[key: string]: CompatApi;
|
|
11
|
+
};
|
|
12
|
+
|
|
3
13
|
export declare function mount(
|
|
4
14
|
component: () => void,
|
|
5
|
-
options: { target: HTMLElement; props?: Record<string, any
|
|
15
|
+
options: { target: HTMLElement; props?: Record<string, any>; compat?: CompatOptions },
|
|
6
16
|
): () => void;
|
|
7
17
|
|
|
8
18
|
export declare function tick(): Promise<void>;
|