ripple 0.2.92 → 0.2.94
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
CHANGED
package/src/compiler/index.js
CHANGED
|
@@ -39,10 +39,11 @@ export function compile(source, filename, options = {}) {
|
|
|
39
39
|
* @returns {object} Volar mappings object
|
|
40
40
|
*/
|
|
41
41
|
export function compile_to_volar_mappings(source, filename) {
|
|
42
|
-
// Parse and transform
|
|
42
|
+
// Parse and transform
|
|
43
43
|
const ast = parse_module(source);
|
|
44
44
|
const analysis = analyze(ast, filename);
|
|
45
45
|
const transformed = transform_client(filename, source, analysis, true);
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
// Create volar mappings directly from the AST instead of relying on esrap's sourcemap
|
|
48
|
+
return convert_source_map_to_mappings(transformed.ast, source, transformed.js.code);
|
|
48
49
|
}
|
|
@@ -1404,12 +1404,7 @@ function transform_ts_child(node, context) {
|
|
|
1404
1404
|
if (is_dom_element) {
|
|
1405
1405
|
children.push(b.jsx_expression_container(b.call(thunk)));
|
|
1406
1406
|
} else {
|
|
1407
|
-
|
|
1408
|
-
const children_id = b.id(children_name);
|
|
1409
|
-
const jsx_id = b.jsx_id('children');
|
|
1410
|
-
jsx_id.loc = node.id.loc;
|
|
1411
|
-
state.init.push(b.const(children_id, thunk));
|
|
1412
|
-
attributes.push(b.jsx_attribute(jsx_id, b.jsx_expression_container(children_id)));
|
|
1407
|
+
attributes.push(b.jsx_attribute(b.jsx_id('children'), b.jsx_expression_container(thunk)));
|
|
1413
1408
|
}
|
|
1414
1409
|
}
|
|
1415
1410
|
|
|
@@ -1463,12 +1458,14 @@ function transform_ts_child(node, context) {
|
|
|
1463
1458
|
state.init.push(b.if(visit(node.test), consequent, alternate));
|
|
1464
1459
|
} else if (node.type === 'ForOfStatement') {
|
|
1465
1460
|
const body_scope = context.state.scopes.get(node.body);
|
|
1466
|
-
const
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1461
|
+
const block_body = transform_body(node.body.body, {
|
|
1462
|
+
...context,
|
|
1463
|
+
state: { ...context.state, scope: body_scope },
|
|
1464
|
+
});
|
|
1465
|
+
if (node.index) {
|
|
1466
|
+
block_body.unshift(b.let(visit(node.index), b.literal(0)));
|
|
1467
|
+
}
|
|
1468
|
+
const body = b.block(block_body);
|
|
1472
1469
|
|
|
1473
1470
|
state.init.push(b.for_of(visit(node.left), visit(node.right), body, node.await));
|
|
1474
1471
|
} else if (node.type === 'TryStatement') {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { walk } from 'zimmerframe';
|
|
2
2
|
|
|
3
3
|
export const mapping_data = {
|
|
4
4
|
verification: true,
|
|
@@ -8,306 +8,483 @@ export const mapping_data = {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* @param {string}
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {number} direction
|
|
16
|
-
*/
|
|
17
|
-
function findTokenBoundary(text, start, direction = 1) {
|
|
18
|
-
if (start < 0 || start >= text.length) return start;
|
|
19
|
-
|
|
20
|
-
let pos = start;
|
|
21
|
-
/** @param {string} c */
|
|
22
|
-
const isAlphaNum = (c) => /[a-zA-Z0-9_$]/.test(c);
|
|
23
|
-
|
|
24
|
-
// If we're at whitespace or punctuation, find the next meaningful character
|
|
25
|
-
while (pos >= 0 && pos < text.length && /\s/.test(text[pos])) {
|
|
26
|
-
pos += direction;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (pos < 0 || pos >= text.length) return start;
|
|
30
|
-
|
|
31
|
-
// If we're in the middle of a word/identifier, find the boundary
|
|
32
|
-
if (isAlphaNum(text[pos])) {
|
|
33
|
-
if (direction > 0) {
|
|
34
|
-
while (pos < text.length && isAlphaNum(text[pos])) pos++;
|
|
35
|
-
} else {
|
|
36
|
-
while (pos >= 0 && isAlphaNum(text[pos])) pos--;
|
|
37
|
-
pos++; // Adjust back to start of token
|
|
38
|
-
}
|
|
39
|
-
} else {
|
|
40
|
-
// For punctuation, just move one character in the given direction
|
|
41
|
-
pos += direction;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return Math.max(0, Math.min(text.length, pos));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Check if source and generated content are meaningfully similar
|
|
49
|
-
* @param {string} sourceContent
|
|
50
|
-
* @param {string} generatedContent
|
|
51
|
-
*/
|
|
52
|
-
function isValidMapping(sourceContent, generatedContent) {
|
|
53
|
-
// Remove whitespace for comparison
|
|
54
|
-
const cleanSource = sourceContent.replace(/\s+/g, '');
|
|
55
|
-
const cleanGenerated = generatedContent.replace(/\s+/g, '');
|
|
56
|
-
|
|
57
|
-
// If either is empty, skip
|
|
58
|
-
if (!cleanSource || !cleanGenerated) return false;
|
|
59
|
-
|
|
60
|
-
// Skip obvious template transformations that don't make sense to map
|
|
61
|
-
const templateTransforms = [
|
|
62
|
-
/^\{.*\}$/, // Curly brace expressions
|
|
63
|
-
/^<.*>$/, // HTML tags
|
|
64
|
-
/^\(\(\)\s*=>\s*\{$/, // Generated function wrappers
|
|
65
|
-
/^\}\)\(\)\}$/, // Generated function closures
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
for (const transform of templateTransforms) {
|
|
69
|
-
if (transform.test(cleanSource) || transform.test(cleanGenerated)) {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Check if content is similar (exact match, or generated contains source)
|
|
75
|
-
if (cleanSource === cleanGenerated) return true;
|
|
76
|
-
if (cleanGenerated.includes(cleanSource)) return true;
|
|
77
|
-
if (cleanSource.includes(cleanGenerated) && cleanGenerated.length > 2) return true;
|
|
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
|
-
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Convert esrap SourceMap to Volar mappings
|
|
97
|
-
* @param {{ mappings: string }} source_map
|
|
98
|
-
* @param {string} source
|
|
99
|
-
* @param {string} generated_code
|
|
11
|
+
* Create Volar mappings by walking the transformed AST
|
|
12
|
+
* @param {any} ast - The transformed AST
|
|
13
|
+
* @param {string} source - Original source code
|
|
14
|
+
* @param {string} generated_code - Generated code from esrap
|
|
100
15
|
* @returns {object}
|
|
101
16
|
*/
|
|
102
|
-
export function convert_source_map_to_mappings(
|
|
17
|
+
export function convert_source_map_to_mappings(ast, source, generated_code) {
|
|
103
18
|
/** @type {Array<{sourceOffsets: number[], generatedOffsets: number[], lengths: number[], data: any}>} */
|
|
104
19
|
const mappings = [];
|
|
105
20
|
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
let generated_offset = 0;
|
|
110
|
-
const generated_lines = generated_code.split('\n');
|
|
111
|
-
const source_lines = source.split('\n');
|
|
21
|
+
// Maintain indices that walk through source and generated code
|
|
22
|
+
let sourceIndex = 0;
|
|
23
|
+
let generatedIndex = 0;
|
|
112
24
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Check if character is a word boundary (not alphanumeric or underscore)
|
|
27
|
+
* @param {string} char
|
|
28
|
+
* @returns {boolean}
|
|
29
|
+
*/
|
|
30
|
+
const isWordBoundary = (char) => {
|
|
31
|
+
return char === undefined || !/[a-zA-Z0-9_$]/.test(char);
|
|
32
|
+
};
|
|
121
33
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Find text in source string, searching character by character from sourceIndex
|
|
36
|
+
* @param {string} text - Text to find
|
|
37
|
+
* @returns {number|null} - Source position or null
|
|
38
|
+
*/
|
|
39
|
+
const findInSource = (text) => {
|
|
40
|
+
for (let i = sourceIndex; i <= source.length - text.length; i++) {
|
|
41
|
+
let match = true;
|
|
42
|
+
for (let j = 0; j < text.length; j++) {
|
|
43
|
+
if (source[i + j] !== text[j]) {
|
|
44
|
+
match = false;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
125
47
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
48
|
+
if (match) {
|
|
49
|
+
// Check word boundaries for identifier-like tokens
|
|
50
|
+
const isIdentifierLike = /^[a-zA-Z_$]/.test(text);
|
|
51
|
+
if (isIdentifierLike) {
|
|
52
|
+
const charBefore = source[i - 1];
|
|
53
|
+
const charAfter = source[i + text.length];
|
|
54
|
+
if (!isWordBoundary(charBefore) || !isWordBoundary(charAfter)) {
|
|
55
|
+
continue; // Not a whole word match, keep searching
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
sourceIndex = i + text.length;
|
|
60
|
+
return i;
|
|
131
61
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const current_generated_offset = generated_offset + generated_column;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
};
|
|
136
65
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// Try different segment boundaries to find the best match
|
|
150
|
-
const candidates = [
|
|
151
|
-
// Forward boundaries
|
|
152
|
-
{
|
|
153
|
-
source: source.substring(source_offset, source_token_end),
|
|
154
|
-
generated: generated_code.substring(current_generated_offset, generated_token_end)
|
|
155
|
-
},
|
|
156
|
-
// Backward boundaries
|
|
157
|
-
{
|
|
158
|
-
source: source.substring(source_token_start, source_offset + 1),
|
|
159
|
-
generated: generated_code.substring(generated_token_start, current_generated_offset + 1)
|
|
160
|
-
},
|
|
161
|
-
// Single character
|
|
162
|
-
{
|
|
163
|
-
source: source.charAt(source_offset),
|
|
164
|
-
generated: generated_code.charAt(current_generated_offset)
|
|
165
|
-
},
|
|
166
|
-
// Try to find exact matches in nearby content
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
// Look for the best candidate match
|
|
170
|
-
let best_match = null;
|
|
171
|
-
for (const candidate of candidates) {
|
|
172
|
-
if (isValidMapping(candidate.source, candidate.generated)) {
|
|
173
|
-
best_match = candidate;
|
|
66
|
+
/**
|
|
67
|
+
* Find text in generated code, searching character by character from generatedIndex
|
|
68
|
+
* @param {string} text - Text to find
|
|
69
|
+
* @returns {number|null} - Generated position or null
|
|
70
|
+
*/
|
|
71
|
+
const findInGenerated = (text) => {
|
|
72
|
+
for (let i = generatedIndex; i <= generated_code.length - text.length; i++) {
|
|
73
|
+
let match = true;
|
|
74
|
+
for (let j = 0; j < text.length; j++) {
|
|
75
|
+
if (generated_code[i + j] !== text[j]) {
|
|
76
|
+
match = false;
|
|
174
77
|
break;
|
|
175
78
|
}
|
|
176
79
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
generated: generatedIdMatch[0]
|
|
187
|
-
};
|
|
80
|
+
if (match) {
|
|
81
|
+
// Check word boundaries for identifier-like tokens
|
|
82
|
+
const isIdentifierLike = /^[a-zA-Z_$]/.test(text);
|
|
83
|
+
if (isIdentifierLike) {
|
|
84
|
+
const charBefore = generated_code[i - 1];
|
|
85
|
+
const charAfter = generated_code[i + text.length];
|
|
86
|
+
if (!isWordBoundary(charBefore) || !isWordBoundary(charAfter)) {
|
|
87
|
+
continue; // Not a whole word match, keep searching
|
|
88
|
+
}
|
|
188
89
|
}
|
|
189
|
-
}
|
|
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
90
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
source: sourceTypeMatch[0],
|
|
200
|
-
generated: generatedTypeMatch[0]
|
|
201
|
-
};
|
|
202
|
-
}
|
|
91
|
+
generatedIndex = i + text.length;
|
|
92
|
+
return i;
|
|
203
93
|
}
|
|
204
|
-
|
|
205
|
-
// Handle special cases for Ripple keywords that might not have generated equivalents
|
|
206
|
-
if (!best_match || best_match.source.length === 0) {
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Special handling for Ripple-specific syntax that may be omitted in generated code
|
|
211
|
-
const sourceAtOffset = source.substring(source_offset, source_offset + 10);
|
|
212
|
-
if (sourceAtOffset.includes('index ')) {
|
|
213
|
-
// For the 'index' keyword, create a mapping even if there's no generated equivalent
|
|
214
|
-
const indexMatch = sourceAtOffset.match(/index\s+/);
|
|
215
|
-
if (indexMatch) {
|
|
216
|
-
best_match = {
|
|
217
|
-
source: indexMatch[0].trim(),
|
|
218
|
-
generated: '' // Empty generated content for keywords that are transformed away
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Skip if we still don't have a valid source match
|
|
224
|
-
if (!best_match || best_match.source.length === 0) {
|
|
225
|
-
continue;
|
|
226
94
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
95
|
+
return null;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Collect text tokens from AST nodes
|
|
99
|
+
/** @type {string[]} */
|
|
100
|
+
const tokens = [];
|
|
101
|
+
|
|
102
|
+
walk(ast, null, {
|
|
103
|
+
_(node, { next, visit }) {
|
|
104
|
+
// Collect key node types: Identifiers, Literals, and JSX Elements
|
|
105
|
+
if (node.type === 'Identifier' && node.name) {
|
|
106
|
+
tokens.push(node.name);
|
|
107
|
+
} else if (node.type === 'JSXIdentifier' && node.name) {
|
|
108
|
+
tokens.push(node.name);
|
|
109
|
+
} else if (node.type === 'Literal' && node.raw) {
|
|
110
|
+
tokens.push(node.raw);
|
|
111
|
+
} else if (node.type === 'ImportDeclaration') {
|
|
112
|
+
// Visit specifiers in source order
|
|
113
|
+
if (node.specifiers) {
|
|
114
|
+
for (const specifier of node.specifiers) {
|
|
115
|
+
visit(specifier);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Skip source (just a string literal)
|
|
119
|
+
return;
|
|
120
|
+
} else if (node.type === 'ImportSpecifier') {
|
|
121
|
+
// If local and imported are the same, only visit local to avoid duplicates
|
|
122
|
+
// Otherwise visit both in order
|
|
123
|
+
if (node.imported && node.local && node.imported.name !== node.local.name) {
|
|
124
|
+
visit(node.imported);
|
|
125
|
+
visit(node.local);
|
|
126
|
+
} else if (node.local) {
|
|
127
|
+
visit(node.local);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
} else if (node.type === 'ImportDefaultSpecifier' || node.type === 'ImportNamespaceSpecifier') {
|
|
131
|
+
// Just visit local
|
|
132
|
+
if (node.local) {
|
|
133
|
+
visit(node.local);
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
} else if (node.type === 'ExportNamedDeclaration') {
|
|
137
|
+
// Visit in source order: declaration, specifiers
|
|
138
|
+
if (node.declaration) {
|
|
139
|
+
visit(node.declaration);
|
|
140
|
+
}
|
|
141
|
+
if (node.specifiers) {
|
|
142
|
+
for (const specifier of node.specifiers) {
|
|
143
|
+
visit(specifier);
|
|
245
144
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
145
|
+
}
|
|
146
|
+
return;
|
|
147
|
+
} else if (node.type === 'ExportDefaultDeclaration') {
|
|
148
|
+
// Visit the declaration
|
|
149
|
+
if (node.declaration) {
|
|
150
|
+
visit(node.declaration);
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
} else if (node.type === 'ExportAllDeclaration') {
|
|
154
|
+
// Nothing to visit (just source string)
|
|
155
|
+
return;
|
|
156
|
+
} else if (node.type === 'JSXElement') {
|
|
157
|
+
// Manually visit in source order: opening element, children, closing element
|
|
158
|
+
|
|
159
|
+
// 1. Visit opening element (name and attributes)
|
|
160
|
+
if (node.openingElement) {
|
|
161
|
+
visit(node.openingElement);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 2. Visit children in order
|
|
165
|
+
if (node.children) {
|
|
166
|
+
for (const child of node.children) {
|
|
167
|
+
visit(child);
|
|
249
168
|
}
|
|
250
169
|
}
|
|
170
|
+
|
|
171
|
+
// 3. Push closing tag name (not visited by AST walker)
|
|
172
|
+
if (!node.openingElement?.selfClosing && node.closingElement?.name?.type === 'JSXIdentifier') {
|
|
173
|
+
tokens.push(node.closingElement.name.name);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return;
|
|
177
|
+
} else if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
178
|
+
// Visit in source order: id, params, body
|
|
179
|
+
if (node.id) {
|
|
180
|
+
visit(node.id);
|
|
181
|
+
}
|
|
182
|
+
if (node.params) {
|
|
183
|
+
for (const param of node.params) {
|
|
184
|
+
visit(param);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (node.body) {
|
|
188
|
+
visit(node.body);
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
} else if (node.type === 'VariableDeclaration') {
|
|
192
|
+
// Visit declarators in order
|
|
193
|
+
if (node.declarations) {
|
|
194
|
+
for (const declarator of node.declarations) {
|
|
195
|
+
visit(declarator);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
} else if (node.type === 'VariableDeclarator') {
|
|
200
|
+
// Visit in source order: id, init
|
|
201
|
+
if (node.id) {
|
|
202
|
+
visit(node.id);
|
|
203
|
+
}
|
|
204
|
+
if (node.init) {
|
|
205
|
+
visit(node.init);
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
} else if (node.type === 'IfStatement') {
|
|
209
|
+
// Visit in source order: test, consequent, alternate
|
|
210
|
+
if (node.test) {
|
|
211
|
+
visit(node.test);
|
|
212
|
+
}
|
|
213
|
+
if (node.consequent) {
|
|
214
|
+
visit(node.consequent);
|
|
215
|
+
}
|
|
216
|
+
if (node.alternate) {
|
|
217
|
+
visit(node.alternate);
|
|
218
|
+
}
|
|
219
|
+
return;
|
|
220
|
+
} else if (node.type === 'ForStatement') {
|
|
221
|
+
// Visit in source order: init, test, update, body
|
|
222
|
+
if (node.init) {
|
|
223
|
+
visit(node.init);
|
|
224
|
+
}
|
|
225
|
+
if (node.test) {
|
|
226
|
+
visit(node.test);
|
|
227
|
+
}
|
|
228
|
+
if (node.update) {
|
|
229
|
+
visit(node.update);
|
|
230
|
+
}
|
|
231
|
+
if (node.body) {
|
|
232
|
+
visit(node.body);
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
} else if (node.type === 'ForOfStatement' || node.type === 'ForInStatement') {
|
|
236
|
+
// Visit in source order: left, right, index (Ripple-specific), body
|
|
237
|
+
if (node.left) {
|
|
238
|
+
visit(node.left);
|
|
239
|
+
}
|
|
240
|
+
if (node.right) {
|
|
241
|
+
visit(node.right);
|
|
242
|
+
}
|
|
243
|
+
// Ripple-specific: index variable
|
|
244
|
+
if (node.index) {
|
|
245
|
+
visit(node.index);
|
|
246
|
+
}
|
|
247
|
+
if (node.body) {
|
|
248
|
+
visit(node.body);
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
} else if (node.type === 'WhileStatement' || node.type === 'DoWhileStatement') {
|
|
252
|
+
// Visit in source order: test, body (while) or body, test (do-while)
|
|
253
|
+
if (node.type === 'WhileStatement') {
|
|
254
|
+
if (node.test) {
|
|
255
|
+
visit(node.test);
|
|
256
|
+
}
|
|
257
|
+
if (node.body) {
|
|
258
|
+
visit(node.body);
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
if (node.body) {
|
|
262
|
+
visit(node.body);
|
|
263
|
+
}
|
|
264
|
+
if (node.test) {
|
|
265
|
+
visit(node.test);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return;
|
|
269
|
+
} else if (node.type === 'TryStatement') {
|
|
270
|
+
// Visit in source order: block, handler, finalizer
|
|
271
|
+
if (node.block) {
|
|
272
|
+
visit(node.block);
|
|
273
|
+
}
|
|
274
|
+
if (node.handler) {
|
|
275
|
+
visit(node.handler);
|
|
276
|
+
}
|
|
277
|
+
if (node.finalizer) {
|
|
278
|
+
visit(node.finalizer);
|
|
279
|
+
}
|
|
280
|
+
return;
|
|
281
|
+
} else if (node.type === 'CatchClause') {
|
|
282
|
+
// Visit in source order: param, body
|
|
283
|
+
if (node.param) {
|
|
284
|
+
visit(node.param);
|
|
285
|
+
}
|
|
286
|
+
if (node.body) {
|
|
287
|
+
visit(node.body);
|
|
288
|
+
}
|
|
289
|
+
return;
|
|
290
|
+
} else if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
|
291
|
+
// Visit in source order: callee, arguments
|
|
292
|
+
if (node.callee) {
|
|
293
|
+
visit(node.callee);
|
|
294
|
+
}
|
|
295
|
+
if (node.arguments) {
|
|
296
|
+
for (const arg of node.arguments) {
|
|
297
|
+
visit(arg);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return;
|
|
301
|
+
} else if (node.type === 'LogicalExpression' || node.type === 'BinaryExpression') {
|
|
302
|
+
// Visit in source order: left, right
|
|
303
|
+
if (node.left) {
|
|
304
|
+
visit(node.left);
|
|
305
|
+
}
|
|
306
|
+
if (node.right) {
|
|
307
|
+
visit(node.right);
|
|
308
|
+
}
|
|
309
|
+
return;
|
|
310
|
+
} else if (node.type === 'MemberExpression') {
|
|
311
|
+
// Visit in source order: object, property
|
|
312
|
+
if (node.object) {
|
|
313
|
+
visit(node.object);
|
|
314
|
+
}
|
|
315
|
+
if (!node.computed && node.property) {
|
|
316
|
+
visit(node.property);
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
} else if (node.type === 'AssignmentExpression' || node.type === 'AssignmentPattern') {
|
|
320
|
+
// Visit in source order: left, right
|
|
321
|
+
if (node.left) {
|
|
322
|
+
visit(node.left);
|
|
323
|
+
}
|
|
324
|
+
if (node.right) {
|
|
325
|
+
visit(node.right);
|
|
326
|
+
}
|
|
327
|
+
return;
|
|
328
|
+
} else if (node.type === 'ObjectExpression' || node.type === 'ObjectPattern') {
|
|
329
|
+
// Visit properties in order
|
|
330
|
+
if (node.properties) {
|
|
331
|
+
for (const prop of node.properties) {
|
|
332
|
+
visit(prop);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return;
|
|
336
|
+
} else if (node.type === 'Property') {
|
|
337
|
+
// Visit in source order: key, value
|
|
338
|
+
if (node.key) {
|
|
339
|
+
visit(node.key);
|
|
340
|
+
}
|
|
341
|
+
if (node.value) {
|
|
342
|
+
visit(node.value);
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
} else if (node.type === 'ArrayExpression' || node.type === 'ArrayPattern') {
|
|
346
|
+
// Visit elements in order
|
|
347
|
+
if (node.elements) {
|
|
348
|
+
for (const element of node.elements) {
|
|
349
|
+
if (element) visit(element);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
} else if (node.type === 'ConditionalExpression') {
|
|
354
|
+
// Visit in source order: test, consequent, alternate
|
|
355
|
+
if (node.test) {
|
|
356
|
+
visit(node.test);
|
|
357
|
+
}
|
|
358
|
+
if (node.consequent) {
|
|
359
|
+
visit(node.consequent);
|
|
360
|
+
}
|
|
361
|
+
if (node.alternate) {
|
|
362
|
+
visit(node.alternate);
|
|
363
|
+
}
|
|
364
|
+
return;
|
|
365
|
+
} else if (node.type === 'UnaryExpression' || node.type === 'UpdateExpression') {
|
|
366
|
+
// Visit argument
|
|
367
|
+
if (node.argument) {
|
|
368
|
+
visit(node.argument);
|
|
369
|
+
}
|
|
370
|
+
return;
|
|
371
|
+
} else if (node.type === 'TemplateLiteral') {
|
|
372
|
+
// Visit quasis and expressions in order
|
|
373
|
+
for (let i = 0; i < node.quasis.length; i++) {
|
|
374
|
+
if (node.quasis[i]) {
|
|
375
|
+
visit(node.quasis[i]);
|
|
376
|
+
}
|
|
377
|
+
if (i < node.expressions.length && node.expressions[i]) {
|
|
378
|
+
visit(node.expressions[i]);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return;
|
|
382
|
+
} else if (node.type === 'TaggedTemplateExpression') {
|
|
383
|
+
// Visit in source order: tag, quasi
|
|
384
|
+
if (node.tag) {
|
|
385
|
+
visit(node.tag);
|
|
386
|
+
}
|
|
387
|
+
if (node.quasi) {
|
|
388
|
+
visit(node.quasi);
|
|
389
|
+
}
|
|
390
|
+
return;
|
|
391
|
+
} else if (node.type === 'ReturnStatement' || node.type === 'ThrowStatement') {
|
|
392
|
+
// Visit argument
|
|
393
|
+
if (node.argument) {
|
|
394
|
+
visit(node.argument);
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
} else if (node.type === 'ExpressionStatement') {
|
|
398
|
+
// Visit expression
|
|
399
|
+
if (node.expression) {
|
|
400
|
+
visit(node.expression);
|
|
401
|
+
}
|
|
402
|
+
return;
|
|
403
|
+
} else if (node.type === 'BlockStatement' || node.type === 'Program') {
|
|
404
|
+
// Visit body statements in order
|
|
405
|
+
if (node.body) {
|
|
406
|
+
for (const statement of node.body) {
|
|
407
|
+
visit(statement);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return;
|
|
411
|
+
} else if (node.type === 'SwitchStatement') {
|
|
412
|
+
// Visit in source order: discriminant, cases
|
|
413
|
+
if (node.discriminant) {
|
|
414
|
+
visit(node.discriminant);
|
|
415
|
+
}
|
|
416
|
+
if (node.cases) {
|
|
417
|
+
for (const caseNode of node.cases) {
|
|
418
|
+
visit(caseNode);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return;
|
|
422
|
+
} else if (node.type === 'SwitchCase') {
|
|
423
|
+
// Visit in source order: test, consequent
|
|
424
|
+
if (node.test) {
|
|
425
|
+
visit(node.test);
|
|
426
|
+
}
|
|
427
|
+
if (node.consequent) {
|
|
428
|
+
for (const statement of node.consequent) {
|
|
429
|
+
visit(statement);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return;
|
|
433
|
+
} else if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {
|
|
434
|
+
// Visit in source order: id, superClass, body
|
|
435
|
+
if (node.id) {
|
|
436
|
+
visit(node.id);
|
|
437
|
+
}
|
|
438
|
+
if (node.superClass) {
|
|
439
|
+
visit(node.superClass);
|
|
440
|
+
}
|
|
441
|
+
if (node.body) {
|
|
442
|
+
visit(node.body);
|
|
443
|
+
}
|
|
444
|
+
return;
|
|
445
|
+
} else if (node.type === 'ClassBody') {
|
|
446
|
+
// Visit body in order
|
|
447
|
+
if (node.body) {
|
|
448
|
+
for (const member of node.body) {
|
|
449
|
+
visit(member);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return;
|
|
453
|
+
} else if (node.type === 'MethodDefinition') {
|
|
454
|
+
// Visit in source order: key, value
|
|
455
|
+
if (node.key) {
|
|
456
|
+
visit(node.key);
|
|
457
|
+
}
|
|
458
|
+
if (node.value) {
|
|
459
|
+
visit(node.value);
|
|
460
|
+
}
|
|
461
|
+
return;
|
|
251
462
|
}
|
|
463
|
+
|
|
464
|
+
next();
|
|
252
465
|
}
|
|
253
|
-
|
|
254
|
-
// Skip mappings for complex RefAttribute syntax to avoid overlapping sourcemaps,
|
|
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
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Allow simple 'ref' keyword mappings for IntelliSense
|
|
263
|
-
if (best_match.source.trim() === 'ref' && best_match.generated.length === 0) {
|
|
264
|
-
// This is just the ref keyword, allow it for syntax support
|
|
265
|
-
// but map it to current position since there's no generated equivalent
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Calculate actual offsets and lengths for the best match
|
|
269
|
-
let actual_source_offset, actual_generated_offset;
|
|
270
|
-
|
|
271
|
-
if (best_match.generated.length > 0) {
|
|
272
|
-
actual_source_offset = source.indexOf(best_match.source, source_offset - best_match.source.length);
|
|
273
|
-
actual_generated_offset = generated_code.indexOf(best_match.generated, current_generated_offset - best_match.generated.length);
|
|
274
|
-
} else {
|
|
275
|
-
// For keywords with no generated equivalent, use the exact source position
|
|
276
|
-
actual_source_offset = source_offset;
|
|
277
|
-
actual_generated_offset = current_generated_offset; // Map to current position in generated code
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Use the match we found, but fall back to original positions if indexOf fails
|
|
281
|
-
const final_source_offset = actual_source_offset !== -1 ? actual_source_offset : source_offset;
|
|
282
|
-
const final_generated_offset = actual_generated_offset !== -1 ? actual_generated_offset : current_generated_offset; // Avoid duplicate mappings by checking if we already have this exact mapping
|
|
283
|
-
const isDuplicate = mappings.some(existing =>
|
|
284
|
-
existing.sourceOffsets[0] === final_source_offset &&
|
|
285
|
-
existing.generatedOffsets[0] === final_generated_offset &&
|
|
286
|
-
existing.lengths[0] === best_match.source.length
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
if (!isDuplicate) {
|
|
290
|
-
mappings.push({
|
|
291
|
-
sourceOffsets: [final_source_offset],
|
|
292
|
-
generatedOffsets: [final_generated_offset],
|
|
293
|
-
lengths: [best_match.source.length],
|
|
294
|
-
data: mapping_data,
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
466
|
+
});
|
|
298
467
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
468
|
+
// Process each token in order
|
|
469
|
+
for (const text of tokens) {
|
|
470
|
+
const sourcePos = findInSource(text);
|
|
471
|
+
const genPos = findInGenerated(text);
|
|
472
|
+
|
|
473
|
+
if (sourcePos !== null && genPos !== null) {
|
|
474
|
+
mappings.push({
|
|
475
|
+
sourceOffsets: [sourcePos],
|
|
476
|
+
generatedOffsets: [genPos],
|
|
477
|
+
lengths: [text.length],
|
|
478
|
+
data: mapping_data,
|
|
479
|
+
});
|
|
303
480
|
}
|
|
304
481
|
}
|
|
305
482
|
|
|
306
|
-
// Sort mappings by source offset
|
|
483
|
+
// Sort mappings by source offset
|
|
307
484
|
mappings.sort((a, b) => a.sourceOffsets[0] - b.sourceOffsets[0]);
|
|
308
485
|
|
|
309
486
|
return {
|
|
310
487
|
code: generated_code,
|
|
311
488
|
mappings,
|
|
312
489
|
};
|
|
313
|
-
}
|
|
490
|
+
}
|