ripple 0.2.91 → 0.2.92
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 +1 -1
- package/src/compiler/index.js +14 -8
- package/src/compiler/phases/1-parse/index.js +55 -7
- package/src/compiler/phases/3-transform/client/index.js +81 -49
- package/src/compiler/phases/3-transform/segments.js +57 -3
- package/src/compiler/scope.js +478 -404
- package/src/compiler/types/index.d.ts +299 -3
- package/src/compiler/utils.js +173 -30
- package/src/runtime/index-client.js +1 -0
- package/src/runtime/internal/client/html.js +18 -8
- package/src/runtime/internal/client/index.js +1 -0
- package/src/runtime/internal/client/portal.js +55 -32
- package/src/runtime/internal/client/render.js +31 -1
- package/src/runtime/internal/client/runtime.js +53 -22
- package/src/utils/normalize_css_property_name.js +23 -0
- package/tests/client/basic.test.ripple +207 -1
- package/tests/client/compiler.test.ripple +95 -1
- package/tests/client/html.test.ripple +29 -1
- package/tests/client/portal.test.ripple +167 -0
- package/types/index.d.ts +2 -0
package/package.json
CHANGED
package/src/compiler/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
/** @import { RawSourceMap } from 'source-map' */
|
|
2
1
|
/** @import { Program } from 'estree' */
|
|
3
|
-
/** @import { ParseError } from './phases/1-parse/index.js' */
|
|
4
2
|
|
|
5
3
|
import { parse as parse_module } from './phases/1-parse/index.js';
|
|
6
4
|
import { analyze } from './phases/2-analyze/index.js';
|
|
@@ -9,18 +7,20 @@ import { transform_server } from './phases/3-transform/server/index.js';
|
|
|
9
7
|
import { convert_source_map_to_mappings } from './phases/3-transform/segments.js';
|
|
10
8
|
|
|
11
9
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @
|
|
10
|
+
* Parse Ripple source code to ESTree AST
|
|
11
|
+
* @param {string} source
|
|
12
|
+
* @returns {Program}
|
|
14
13
|
*/
|
|
15
14
|
export function parse(source) {
|
|
16
15
|
return parse_module(source);
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
|
-
*
|
|
21
|
-
* @param {string}
|
|
22
|
-
* @param {
|
|
23
|
-
* @
|
|
19
|
+
* Compile Ripple source code to JS/CSS output
|
|
20
|
+
* @param {string} source
|
|
21
|
+
* @param {string} filename
|
|
22
|
+
* @param {{ mode?: 'client' | 'server' }} [options]
|
|
23
|
+
* @returns {object}
|
|
24
24
|
*/
|
|
25
25
|
export function compile(source, filename, options = {}) {
|
|
26
26
|
const ast = parse_module(source);
|
|
@@ -32,6 +32,12 @@ export function compile(source, filename, options = {}) {
|
|
|
32
32
|
return result;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Compile Ripple source to Volar mappings for editor integration
|
|
37
|
+
* @param {string} source
|
|
38
|
+
* @param {string} filename
|
|
39
|
+
* @returns {object} Volar mappings object
|
|
40
|
+
*/
|
|
35
41
|
export function compile_to_volar_mappings(source, filename) {
|
|
36
42
|
// Parse and transform to get the esrap sourcemap
|
|
37
43
|
const ast = parse_module(source);
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/** @import { Program } from 'estree' */
|
|
2
|
+
/** @import {
|
|
3
|
+
* CommentWithLocation,
|
|
4
|
+
* RipplePluginConfig
|
|
5
|
+
* } from '#compiler' */
|
|
6
|
+
|
|
1
7
|
import * as acorn from 'acorn';
|
|
2
8
|
import { tsPlugin } from 'acorn-typescript';
|
|
3
9
|
import { parse_style } from './style.js';
|
|
@@ -6,6 +12,11 @@ import { regex_newline_characters } from '../../../utils/patterns.js';
|
|
|
6
12
|
|
|
7
13
|
const parser = acorn.Parser.extend(tsPlugin({ allowSatisfies: true }), RipplePlugin());
|
|
8
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Convert JSX node types to regular JavaScript node types
|
|
17
|
+
* @param {any} node - The JSX node to convert
|
|
18
|
+
* @returns {any} The converted node
|
|
19
|
+
*/
|
|
9
20
|
function convert_from_jsx(node) {
|
|
10
21
|
if (node.type === 'JSXIdentifier') {
|
|
11
22
|
node.type = 'Identifier';
|
|
@@ -17,16 +28,26 @@ function convert_from_jsx(node) {
|
|
|
17
28
|
return node;
|
|
18
29
|
}
|
|
19
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Acorn parser plugin for Ripple syntax extensions
|
|
33
|
+
* @param {RipplePluginConfig} [config] - Plugin configuration
|
|
34
|
+
* @returns {function(any): any} Parser extension function
|
|
35
|
+
*/
|
|
20
36
|
function RipplePlugin(config) {
|
|
21
|
-
return (Parser) => {
|
|
37
|
+
return (/** @type {any} */ Parser) => {
|
|
22
38
|
const original = acorn.Parser.prototype;
|
|
23
39
|
const tt = Parser.tokTypes || acorn.tokTypes;
|
|
24
40
|
const tc = Parser.tokContexts || acorn.tokContexts;
|
|
25
41
|
|
|
26
42
|
class RippleParser extends Parser {
|
|
43
|
+
/** @type {any[]} */
|
|
27
44
|
#path = [];
|
|
28
45
|
|
|
29
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Helper method to get the element name from a JSX identifier or member expression
|
|
48
|
+
* @param {any} node - The node to get the name from
|
|
49
|
+
* @returns {string | null} Element name or null
|
|
50
|
+
*/
|
|
30
51
|
getElementName(node) {
|
|
31
52
|
if (!node) return null;
|
|
32
53
|
if (node.type === 'Identifier' || node.type === 'JSXIdentifier') {
|
|
@@ -38,6 +59,11 @@ function RipplePlugin(config) {
|
|
|
38
59
|
return null;
|
|
39
60
|
}
|
|
40
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Get token from character code - handles Ripple-specific tokens
|
|
64
|
+
* @param {number} code - Character code
|
|
65
|
+
* @returns {any} Token or calls super method
|
|
66
|
+
*/
|
|
41
67
|
getTokenFromCode(code) {
|
|
42
68
|
if (code === 60) {
|
|
43
69
|
// < character
|
|
@@ -135,7 +161,10 @@ function RipplePlugin(config) {
|
|
|
135
161
|
return super.getTokenFromCode(code);
|
|
136
162
|
}
|
|
137
163
|
|
|
138
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Read an @ prefixed identifier
|
|
166
|
+
* @returns {any} Token with @ identifier
|
|
167
|
+
*/
|
|
139
168
|
readAtIdentifier() {
|
|
140
169
|
const start = this.pos;
|
|
141
170
|
this.pos++; // skip '@'
|
|
@@ -166,7 +195,11 @@ function RipplePlugin(config) {
|
|
|
166
195
|
return this.finishToken(tt.name, '@' + word);
|
|
167
196
|
}
|
|
168
197
|
|
|
169
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Override parseIdent to mark @ identifiers as tracked
|
|
200
|
+
* @param {any} [liberal] - Whether to allow liberal parsing
|
|
201
|
+
* @returns {any} Parsed identifier node
|
|
202
|
+
*/
|
|
170
203
|
parseIdent(liberal) {
|
|
171
204
|
const node = super.parseIdent(liberal);
|
|
172
205
|
if (node.name && node.name.startsWith('@')) {
|
|
@@ -181,6 +214,13 @@ function RipplePlugin(config) {
|
|
|
181
214
|
return node;
|
|
182
215
|
}
|
|
183
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Parse expression atom - handles TrackedArray and TrackedObject literals
|
|
219
|
+
* @param {any} [refDestructuringErrors]
|
|
220
|
+
* @param {any} [forNew]
|
|
221
|
+
* @param {any} [forInit]
|
|
222
|
+
* @returns {any} Parsed expression atom
|
|
223
|
+
*/
|
|
184
224
|
parseExprAtom(refDestructuringErrors, forNew, forInit) {
|
|
185
225
|
// Check if this is a tuple literal starting with #[
|
|
186
226
|
if (this.type === tt.bracketL && this.value === '#[') {
|
|
@@ -818,6 +858,7 @@ function RipplePlugin(config) {
|
|
|
818
858
|
const element = this.startNode();
|
|
819
859
|
element.start = position.index;
|
|
820
860
|
element.loc.start = position;
|
|
861
|
+
element.metadata = {};
|
|
821
862
|
element.type = 'Element';
|
|
822
863
|
this.#path.push(element);
|
|
823
864
|
element.children = [];
|
|
@@ -1150,7 +1191,8 @@ function RipplePlugin(config) {
|
|
|
1150
1191
|
* in JS code and so that `prettier-plugin-ripple` doesn't remove all comments when formatting.
|
|
1151
1192
|
* @param {string} source
|
|
1152
1193
|
* @param {CommentWithLocation[]} comments
|
|
1153
|
-
* @param {number} index
|
|
1194
|
+
* @param {number} [index=0] - Starting index
|
|
1195
|
+
* @returns {{ onComment: Function, add_comments: Function }} Comment handler functions
|
|
1154
1196
|
*/
|
|
1155
1197
|
function get_comment_handlers(source, comments, index = 0) {
|
|
1156
1198
|
return {
|
|
@@ -1245,7 +1287,13 @@ function get_comment_handlers(source, comments, index = 0) {
|
|
|
1245
1287
|
};
|
|
1246
1288
|
}
|
|
1247
1289
|
|
|
1290
|
+
/**
|
|
1291
|
+
* Parse Ripple source code into an AST
|
|
1292
|
+
* @param {string} source
|
|
1293
|
+
* @returns {Program}
|
|
1294
|
+
*/
|
|
1248
1295
|
export function parse(source) {
|
|
1296
|
+
/** @type {CommentWithLocation[]} */
|
|
1249
1297
|
const comments = [];
|
|
1250
1298
|
const { onComment, add_comments } = get_comment_handlers(source, comments);
|
|
1251
1299
|
let ast;
|
|
@@ -1255,7 +1303,7 @@ export function parse(source) {
|
|
|
1255
1303
|
sourceType: 'module',
|
|
1256
1304
|
ecmaVersion: 13,
|
|
1257
1305
|
locations: true,
|
|
1258
|
-
onComment,
|
|
1306
|
+
onComment: /** @type {any} */ (onComment),
|
|
1259
1307
|
});
|
|
1260
1308
|
} catch (e) {
|
|
1261
1309
|
throw e;
|
|
@@ -1263,5 +1311,5 @@ export function parse(source) {
|
|
|
1263
1311
|
|
|
1264
1312
|
add_comments(ast);
|
|
1265
1313
|
|
|
1266
|
-
return ast;
|
|
1314
|
+
return /** @type {Program} */ (ast);
|
|
1267
1315
|
}
|
|
@@ -544,6 +544,7 @@ const visitors = {
|
|
|
544
544
|
|
|
545
545
|
if (is_dom_element) {
|
|
546
546
|
let class_attribute = null;
|
|
547
|
+
let style_attribute = null;
|
|
547
548
|
const local_updates = [];
|
|
548
549
|
const is_void = is_void_element(node.id.name);
|
|
549
550
|
|
|
@@ -559,7 +560,7 @@ const visitors = {
|
|
|
559
560
|
continue;
|
|
560
561
|
}
|
|
561
562
|
|
|
562
|
-
if (attr.value.type === 'Literal' && name !== 'class') {
|
|
563
|
+
if (attr.value.type === 'Literal' && name !== 'class' && name !== 'style') {
|
|
563
564
|
handle_static_attr(name, attr.value.value);
|
|
564
565
|
continue;
|
|
565
566
|
}
|
|
@@ -570,6 +571,12 @@ const visitors = {
|
|
|
570
571
|
continue;
|
|
571
572
|
}
|
|
572
573
|
|
|
574
|
+
if (name === 'style') {
|
|
575
|
+
style_attribute = attr;
|
|
576
|
+
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
|
|
573
580
|
if (name === 'value') {
|
|
574
581
|
const id = state.flush_node();
|
|
575
582
|
const metadata = { tracking: false, await: false };
|
|
@@ -742,6 +749,25 @@ const visitors = {
|
|
|
742
749
|
handle_static_attr(is_spreading ? '#class' : 'class', value);
|
|
743
750
|
}
|
|
744
751
|
|
|
752
|
+
if (style_attribute !== null) {
|
|
753
|
+
if (style_attribute.value.type === 'Literal') {
|
|
754
|
+
handle_static_attr(style_attribute.name.name, style_attribute.value.value);
|
|
755
|
+
} else {
|
|
756
|
+
const id = state.flush_node();
|
|
757
|
+
const metadata = { tracking: false, await: false };
|
|
758
|
+
const expression = visit(style_attribute.value, { ...state, metadata });
|
|
759
|
+
const name = style_attribute.name.name;
|
|
760
|
+
|
|
761
|
+
const statement = b.stmt(b.call('_$_.set_attribute', id, b.literal(name), expression));
|
|
762
|
+
|
|
763
|
+
if (metadata.tracking) {
|
|
764
|
+
local_updates.push(statement);
|
|
765
|
+
} else {
|
|
766
|
+
state.init.push(statement);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
745
771
|
state.template.push('>');
|
|
746
772
|
|
|
747
773
|
if (spread_attributes !== null && spread_attributes.length > 0) {
|
|
@@ -799,13 +825,17 @@ const visitors = {
|
|
|
799
825
|
}
|
|
800
826
|
|
|
801
827
|
props.push(
|
|
802
|
-
b.prop(
|
|
828
|
+
b.prop(
|
|
829
|
+
'get',
|
|
830
|
+
b.key(attr.name.name),
|
|
831
|
+
b.function(null, [], b.block([b.return(property)])),
|
|
832
|
+
),
|
|
803
833
|
);
|
|
804
834
|
} else {
|
|
805
|
-
props.push(b.prop('init', attr.name, property));
|
|
835
|
+
props.push(b.prop('init', b.key(attr.name.name), property));
|
|
806
836
|
}
|
|
807
837
|
} else {
|
|
808
|
-
props.push(b.prop('init', attr.name, visit(attr.value, state)));
|
|
838
|
+
props.push(b.prop('init', b.key(attr.name.name), visit(attr.value, state)));
|
|
809
839
|
}
|
|
810
840
|
} else if (attr.type === 'SpreadAttribute') {
|
|
811
841
|
props.push(
|
|
@@ -1317,54 +1347,46 @@ function transform_ts_child(node, context) {
|
|
|
1317
1347
|
const children = [];
|
|
1318
1348
|
let has_children_props = false;
|
|
1319
1349
|
|
|
1320
|
-
// Filter out RefAttributes and handle them separately
|
|
1321
1350
|
const ref_attributes = [];
|
|
1322
|
-
const attributes = node.attributes
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1351
|
+
const attributes = node.attributes.map((attr) => {
|
|
1352
|
+
if (attr.type === 'Attribute') {
|
|
1353
|
+
const metadata = { await: false };
|
|
1354
|
+
const name = visit(attr.name, { ...state, metadata });
|
|
1355
|
+
const value =
|
|
1356
|
+
attr.value === null ? b.literal(true) : visit(attr.value, { ...state, metadata });
|
|
1357
|
+
|
|
1358
|
+
// Handle both regular identifiers and tracked identifiers
|
|
1359
|
+
let prop_name;
|
|
1360
|
+
if (name.type === 'Identifier') {
|
|
1361
|
+
prop_name = name.name;
|
|
1362
|
+
} else if (name.type === 'MemberExpression' && name.object.type === 'Identifier') {
|
|
1363
|
+
// For tracked attributes like {@count}, use the original name
|
|
1364
|
+
prop_name = name.object.name;
|
|
1365
|
+
} else {
|
|
1366
|
+
prop_name = attr.name.name || 'unknown';
|
|
1327
1367
|
}
|
|
1328
|
-
return true;
|
|
1329
|
-
})
|
|
1330
|
-
.map((attr) => {
|
|
1331
|
-
if (attr.type === 'Attribute') {
|
|
1332
|
-
const metadata = { await: false };
|
|
1333
|
-
const name = visit(attr.name, { ...state, metadata });
|
|
1334
|
-
const value =
|
|
1335
|
-
attr.value === null ? b.literal(true) : visit(attr.value, { ...state, metadata });
|
|
1336
|
-
|
|
1337
|
-
// Handle both regular identifiers and tracked identifiers
|
|
1338
|
-
let prop_name;
|
|
1339
|
-
if (name.type === 'Identifier') {
|
|
1340
|
-
prop_name = name.name;
|
|
1341
|
-
} else if (name.type === 'MemberExpression' && name.object.type === 'Identifier') {
|
|
1342
|
-
// For tracked attributes like {@count}, use the original name
|
|
1343
|
-
prop_name = name.object.name;
|
|
1344
|
-
} else {
|
|
1345
|
-
prop_name = attr.name.name || 'unknown';
|
|
1346
|
-
}
|
|
1347
1368
|
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
}
|
|
1352
|
-
jsx_name.loc = attr.name.loc || name.loc;
|
|
1353
|
-
|
|
1354
|
-
return b.jsx_attribute(jsx_name, b.jsx_expression_container(value));
|
|
1355
|
-
} else if (attr.type === 'SpreadAttribute') {
|
|
1356
|
-
const metadata = { await: false };
|
|
1357
|
-
const argument = visit(attr.argument, { ...state, metadata });
|
|
1358
|
-
return b.jsx_spread_attribute(argument);
|
|
1369
|
+
const jsx_name = b.jsx_id(prop_name);
|
|
1370
|
+
if (prop_name === 'children') {
|
|
1371
|
+
has_children_props = true;
|
|
1359
1372
|
}
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1373
|
+
jsx_name.loc = attr.name.loc || name.loc;
|
|
1374
|
+
|
|
1375
|
+
return b.jsx_attribute(jsx_name, b.jsx_expression_container(value));
|
|
1376
|
+
} else if (attr.type === 'SpreadAttribute') {
|
|
1377
|
+
const metadata = { await: false };
|
|
1378
|
+
const argument = visit(attr.argument, { ...state, metadata });
|
|
1379
|
+
return b.jsx_spread_attribute(argument);
|
|
1380
|
+
} else if (attr.type === 'RefAttribute') {
|
|
1381
|
+
if (!context.state.imports.has(`import { createRefKey } from 'ripple'`)) {
|
|
1382
|
+
context.state.imports.add(`import { createRefKey } from 'ripple'`);
|
|
1383
|
+
}
|
|
1384
|
+
const metadata = { await: false };
|
|
1385
|
+
const argument = visit(attr.argument, { ...state, metadata });
|
|
1386
|
+
const wrapper = b.object([b.prop('init', b.call('createRefKey'), argument, true)]);
|
|
1387
|
+
return b.jsx_spread_attribute(wrapper);
|
|
1388
|
+
}
|
|
1389
|
+
});
|
|
1368
1390
|
|
|
1369
1391
|
if (!node.selfClosing && !has_children_props && node.children.length > 0) {
|
|
1370
1392
|
const is_dom_element = is_element_dom_element(node);
|
|
@@ -1607,7 +1629,17 @@ function transform_children(children, context) {
|
|
|
1607
1629
|
context.state.template.push('<!>');
|
|
1608
1630
|
|
|
1609
1631
|
const id = flush_node();
|
|
1610
|
-
state.update.push(
|
|
1632
|
+
state.update.push(
|
|
1633
|
+
b.stmt(
|
|
1634
|
+
b.call(
|
|
1635
|
+
'_$_.html',
|
|
1636
|
+
id,
|
|
1637
|
+
b.thunk(expression),
|
|
1638
|
+
state.namespace === 'svg' && b.true,
|
|
1639
|
+
state.namespace === 'mathml' && b.true,
|
|
1640
|
+
),
|
|
1641
|
+
),
|
|
1642
|
+
);
|
|
1611
1643
|
} else if (node.type === 'Text') {
|
|
1612
1644
|
const metadata = { tracking: false, await: false };
|
|
1613
1645
|
const expression = visit(node.expression, { ...state, metadata });
|
|
@@ -76,6 +76,19 @@ function isValidMapping(sourceContent, generatedContent) {
|
|
|
76
76
|
if (cleanGenerated.includes(cleanSource)) return true;
|
|
77
77
|
if (cleanSource.includes(cleanGenerated) && cleanGenerated.length > 2) return true;
|
|
78
78
|
|
|
79
|
+
// Special handling for ref callback parameters and types in createRefKey context
|
|
80
|
+
if (sourceContent.match(/\w+:\s*\w+/) && generatedContent.match(/\w+:\s*\w+/)) {
|
|
81
|
+
// This looks like a parameter with type annotation, allow mapping
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Allow mapping of identifiers that appear in both source and generated
|
|
86
|
+
if (sourceContent.match(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/) &&
|
|
87
|
+
generatedContent.match(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/) &&
|
|
88
|
+
sourceContent === generatedContent) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
79
92
|
return false;
|
|
80
93
|
}
|
|
81
94
|
|
|
@@ -175,6 +188,20 @@ export function convert_source_map_to_mappings(source_map, source, generated_cod
|
|
|
175
188
|
}
|
|
176
189
|
}
|
|
177
190
|
|
|
191
|
+
// Special handling for type annotations in ref callbacks
|
|
192
|
+
if (!best_match) {
|
|
193
|
+
// Look for type annotations like "HTMLButtonElement"
|
|
194
|
+
const sourceTypeMatch = source.substring(source_offset).match(/^[A-Z][a-zA-Z0-9]*(?:Element|Type|Interface)?/);
|
|
195
|
+
const generatedTypeMatch = generated_code.substring(current_generated_offset).match(/^[A-Z][a-zA-Z0-9]*(?:Element|Type|Interface)?/);
|
|
196
|
+
|
|
197
|
+
if (sourceTypeMatch && generatedTypeMatch && sourceTypeMatch[0] === generatedTypeMatch[0]) {
|
|
198
|
+
best_match = {
|
|
199
|
+
source: sourceTypeMatch[0],
|
|
200
|
+
generated: generatedTypeMatch[0]
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
178
205
|
// Handle special cases for Ripple keywords that might not have generated equivalents
|
|
179
206
|
if (!best_match || best_match.source.length === 0) {
|
|
180
207
|
continue;
|
|
@@ -198,10 +225,37 @@ export function convert_source_map_to_mappings(source_map, source, generated_cod
|
|
|
198
225
|
continue;
|
|
199
226
|
}
|
|
200
227
|
|
|
228
|
+
// Handle special ref syntax mapping for createRefKey() pattern
|
|
229
|
+
const sourceAtRefOffset = source.substring(Math.max(0, source_offset - 20), source_offset + 20);
|
|
230
|
+
const generatedAtRefOffset = generated_code.substring(Math.max(0, current_generated_offset - 20), current_generated_offset + 20);
|
|
231
|
+
|
|
232
|
+
// Check if we're dealing with ref callback syntax in source and createRefKey in generated
|
|
233
|
+
if (sourceAtRefOffset.includes('{ref ') && generatedAtRefOffset.includes('createRefKey')) {
|
|
234
|
+
// Look for the ref callback pattern in source: {ref (param: Type) => { ... }}
|
|
235
|
+
const refMatch = source.substring(source_offset - 50, source_offset + 50).match(/\{ref\s*\(([^)]+)\)\s*=>/);
|
|
236
|
+
if (refMatch) {
|
|
237
|
+
const paramMatch = refMatch[1].match(/(\w+):\s*(\w+)/);
|
|
238
|
+
if (paramMatch) {
|
|
239
|
+
const paramName = paramMatch[1];
|
|
240
|
+
const typeName = paramMatch[2];
|
|
241
|
+
|
|
242
|
+
// Map the parameter name to the generated callback parameter
|
|
243
|
+
if (best_match.source === paramName || best_match.source.includes(paramName)) {
|
|
244
|
+
// This is a ref callback parameter, allow the mapping
|
|
245
|
+
}
|
|
246
|
+
// Map the type annotation
|
|
247
|
+
else if (best_match.source === typeName || best_match.source.includes(typeName)) {
|
|
248
|
+
// This is a type annotation in ref callback, allow the mapping
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
201
254
|
// Skip mappings for complex RefAttribute syntax to avoid overlapping sourcemaps,
|
|
202
|
-
// but allow
|
|
203
|
-
if (best_match.source.includes('{ref ') && best_match.source.length > 10
|
|
204
|
-
|
|
255
|
+
// but allow mappings that are part of the createRefKey pattern
|
|
256
|
+
if (best_match.source.includes('{ref ') && best_match.source.length > 10 &&
|
|
257
|
+
!generatedAtRefOffset.includes('createRefKey')) {
|
|
258
|
+
// Skip complex ref expressions like '{ref (node) => { ... }}' only if not using createRefKey
|
|
205
259
|
continue;
|
|
206
260
|
}
|
|
207
261
|
|