ripple 0.2.92 → 0.2.93
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
|
|
|
@@ -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,450 @@ 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');
|
|
112
|
-
|
|
113
|
-
// Process each line of generated code
|
|
114
|
-
for (let generated_line = 0; generated_line < generated_lines.length; generated_line++) {
|
|
115
|
-
const line = generated_lines[generated_line];
|
|
116
|
-
const line_mappings = decoded_mappings[generated_line] || [];
|
|
21
|
+
// Maintain indices that walk through source and generated code
|
|
22
|
+
let sourceIndex = 0;
|
|
23
|
+
let generatedIndex = 0;
|
|
117
24
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Find text in source string, searching character by character from sourceIndex
|
|
27
|
+
* @param {string} text - Text to find
|
|
28
|
+
* @returns {number|null} - Source position or null
|
|
29
|
+
*/
|
|
30
|
+
const findInSource = (text) => {
|
|
31
|
+
for (let i = sourceIndex; i <= source.length - text.length; i++) {
|
|
32
|
+
let match = true;
|
|
33
|
+
for (let j = 0; j < text.length; j++) {
|
|
34
|
+
if (source[i + j] !== text[j]) {
|
|
35
|
+
match = false;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
125
38
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
for (let i = 0; i < Math.min(source_line, source_lines.length - 1); i++) {
|
|
130
|
-
source_offset += source_lines[i].length + 1; // +1 for newline
|
|
39
|
+
if (match) {
|
|
40
|
+
sourceIndex = i + text.length;
|
|
41
|
+
return i;
|
|
131
42
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const current_generated_offset = generated_offset + generated_column;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
};
|
|
136
46
|
|
|
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;
|
|
47
|
+
/**
|
|
48
|
+
* Find text in generated code, searching character by character from generatedIndex
|
|
49
|
+
* @param {string} text - Text to find
|
|
50
|
+
* @returns {number|null} - Generated position or null
|
|
51
|
+
*/
|
|
52
|
+
const findInGenerated = (text) => {
|
|
53
|
+
for (let i = generatedIndex; i <= generated_code.length - text.length; i++) {
|
|
54
|
+
let match = true;
|
|
55
|
+
for (let j = 0; j < text.length; j++) {
|
|
56
|
+
if (generated_code[i + j] !== text[j]) {
|
|
57
|
+
match = false;
|
|
174
58
|
break;
|
|
175
59
|
}
|
|
176
60
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
61
|
+
if (match) {
|
|
62
|
+
generatedIndex = i + text.length;
|
|
63
|
+
return i;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Collect text tokens from AST nodes
|
|
70
|
+
/** @type {string[]} */
|
|
71
|
+
const tokens = [];
|
|
72
|
+
|
|
73
|
+
walk(ast, null, {
|
|
74
|
+
_(node, { next, visit }) {
|
|
75
|
+
// Collect key node types: Identifiers, Literals, and JSX Elements
|
|
76
|
+
if (node.type === 'Identifier' && node.name) {
|
|
77
|
+
tokens.push(node.name);
|
|
78
|
+
} else if (node.type === 'JSXIdentifier' && node.name) {
|
|
79
|
+
tokens.push(node.name);
|
|
80
|
+
} else if (node.type === 'Literal' && node.raw) {
|
|
81
|
+
tokens.push(node.raw);
|
|
82
|
+
} else if (node.type === 'ImportDeclaration') {
|
|
83
|
+
// Visit specifiers in source order
|
|
84
|
+
if (node.specifiers) {
|
|
85
|
+
for (const specifier of node.specifiers) {
|
|
86
|
+
visit(specifier);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Skip source (just a string literal)
|
|
90
|
+
return;
|
|
91
|
+
} else if (node.type === 'ImportSpecifier') {
|
|
92
|
+
// If local and imported are the same, only visit local to avoid duplicates
|
|
93
|
+
// Otherwise visit both in order
|
|
94
|
+
if (node.imported && node.local && node.imported.name !== node.local.name) {
|
|
95
|
+
visit(node.imported);
|
|
96
|
+
visit(node.local);
|
|
97
|
+
} else if (node.local) {
|
|
98
|
+
visit(node.local);
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
} else if (node.type === 'ImportDefaultSpecifier' || node.type === 'ImportNamespaceSpecifier') {
|
|
102
|
+
// Just visit local
|
|
103
|
+
if (node.local) {
|
|
104
|
+
visit(node.local);
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
} else if (node.type === 'ExportNamedDeclaration') {
|
|
108
|
+
// Visit in source order: declaration, specifiers
|
|
109
|
+
if (node.declaration) {
|
|
110
|
+
visit(node.declaration);
|
|
111
|
+
}
|
|
112
|
+
if (node.specifiers) {
|
|
113
|
+
for (const specifier of node.specifiers) {
|
|
114
|
+
visit(specifier);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
} else if (node.type === 'ExportDefaultDeclaration') {
|
|
119
|
+
// Visit the declaration
|
|
120
|
+
if (node.declaration) {
|
|
121
|
+
visit(node.declaration);
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
} else if (node.type === 'ExportAllDeclaration') {
|
|
125
|
+
// Nothing to visit (just source string)
|
|
126
|
+
return;
|
|
127
|
+
} else if (node.type === 'JSXElement') {
|
|
128
|
+
// Manually visit in source order: opening element, children, closing element
|
|
182
129
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
generated: generatedIdMatch[0]
|
|
187
|
-
};
|
|
130
|
+
// 1. Visit opening element (name and attributes)
|
|
131
|
+
if (node.openingElement) {
|
|
132
|
+
visit(node.openingElement);
|
|
188
133
|
}
|
|
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
134
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
135
|
+
// 2. Visit children in order
|
|
136
|
+
if (node.children) {
|
|
137
|
+
for (const child of node.children) {
|
|
138
|
+
visit(child);
|
|
139
|
+
}
|
|
202
140
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
}
|
|
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
|
|
141
|
+
|
|
142
|
+
// 3. Push closing tag name (not visited by AST walker)
|
|
143
|
+
if (!node.openingElement?.selfClosing && node.closingElement?.name?.type === 'JSXIdentifier') {
|
|
144
|
+
tokens.push(node.closingElement.name.name);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return;
|
|
148
|
+
} else if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
149
|
+
// Visit in source order: id, params, body
|
|
150
|
+
if (node.id) {
|
|
151
|
+
visit(node.id);
|
|
152
|
+
}
|
|
153
|
+
if (node.params) {
|
|
154
|
+
for (const param of node.params) {
|
|
155
|
+
visit(param);
|
|
245
156
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
157
|
+
}
|
|
158
|
+
if (node.body) {
|
|
159
|
+
visit(node.body);
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
} else if (node.type === 'VariableDeclaration') {
|
|
163
|
+
// Visit declarators in order
|
|
164
|
+
if (node.declarations) {
|
|
165
|
+
for (const declarator of node.declarations) {
|
|
166
|
+
visit(declarator);
|
|
249
167
|
}
|
|
250
168
|
}
|
|
169
|
+
return;
|
|
170
|
+
} else if (node.type === 'VariableDeclarator') {
|
|
171
|
+
// Visit in source order: id, init
|
|
172
|
+
if (node.id) {
|
|
173
|
+
visit(node.id);
|
|
174
|
+
}
|
|
175
|
+
if (node.init) {
|
|
176
|
+
visit(node.init);
|
|
177
|
+
}
|
|
178
|
+
return;
|
|
179
|
+
} else if (node.type === 'IfStatement') {
|
|
180
|
+
// Visit in source order: test, consequent, alternate
|
|
181
|
+
if (node.test) {
|
|
182
|
+
visit(node.test);
|
|
183
|
+
}
|
|
184
|
+
if (node.consequent) {
|
|
185
|
+
visit(node.consequent);
|
|
186
|
+
}
|
|
187
|
+
if (node.alternate) {
|
|
188
|
+
visit(node.alternate);
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
} else if (node.type === 'ForStatement') {
|
|
192
|
+
// Visit in source order: init, test, update, body
|
|
193
|
+
if (node.init) {
|
|
194
|
+
visit(node.init);
|
|
195
|
+
}
|
|
196
|
+
if (node.test) {
|
|
197
|
+
visit(node.test);
|
|
198
|
+
}
|
|
199
|
+
if (node.update) {
|
|
200
|
+
visit(node.update);
|
|
201
|
+
}
|
|
202
|
+
if (node.body) {
|
|
203
|
+
visit(node.body);
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
} else if (node.type === 'ForOfStatement' || node.type === 'ForInStatement') {
|
|
207
|
+
// Visit in source order: left, right, body
|
|
208
|
+
if (node.left) {
|
|
209
|
+
visit(node.left);
|
|
210
|
+
}
|
|
211
|
+
if (node.right) {
|
|
212
|
+
visit(node.right);
|
|
213
|
+
}
|
|
214
|
+
if (node.body) {
|
|
215
|
+
visit(node.body);
|
|
216
|
+
}
|
|
217
|
+
return;
|
|
218
|
+
} else if (node.type === 'WhileStatement' || node.type === 'DoWhileStatement') {
|
|
219
|
+
// Visit in source order: test, body (while) or body, test (do-while)
|
|
220
|
+
if (node.type === 'WhileStatement') {
|
|
221
|
+
if (node.test) {
|
|
222
|
+
visit(node.test);
|
|
223
|
+
}
|
|
224
|
+
if (node.body) {
|
|
225
|
+
visit(node.body);
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
if (node.body) {
|
|
229
|
+
visit(node.body);
|
|
230
|
+
}
|
|
231
|
+
if (node.test) {
|
|
232
|
+
visit(node.test);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return;
|
|
236
|
+
} else if (node.type === 'TryStatement') {
|
|
237
|
+
// Visit in source order: block, handler, finalizer
|
|
238
|
+
if (node.block) {
|
|
239
|
+
visit(node.block);
|
|
240
|
+
}
|
|
241
|
+
if (node.handler) {
|
|
242
|
+
visit(node.handler);
|
|
243
|
+
}
|
|
244
|
+
if (node.finalizer) {
|
|
245
|
+
visit(node.finalizer);
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
} else if (node.type === 'CatchClause') {
|
|
249
|
+
// Visit in source order: param, body
|
|
250
|
+
if (node.param) {
|
|
251
|
+
visit(node.param);
|
|
252
|
+
}
|
|
253
|
+
if (node.body) {
|
|
254
|
+
visit(node.body);
|
|
255
|
+
}
|
|
256
|
+
return;
|
|
257
|
+
} else if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
|
258
|
+
// Visit in source order: callee, arguments
|
|
259
|
+
if (node.callee) {
|
|
260
|
+
visit(node.callee);
|
|
261
|
+
}
|
|
262
|
+
if (node.arguments) {
|
|
263
|
+
for (const arg of node.arguments) {
|
|
264
|
+
visit(arg);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
} else if (node.type === 'LogicalExpression' || node.type === 'BinaryExpression') {
|
|
269
|
+
// Visit in source order: left, right
|
|
270
|
+
if (node.left) {
|
|
271
|
+
visit(node.left);
|
|
272
|
+
}
|
|
273
|
+
if (node.right) {
|
|
274
|
+
visit(node.right);
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
} else if (node.type === 'MemberExpression') {
|
|
278
|
+
// Visit in source order: object, property
|
|
279
|
+
if (node.object) {
|
|
280
|
+
visit(node.object);
|
|
281
|
+
}
|
|
282
|
+
if (!node.computed && node.property) {
|
|
283
|
+
visit(node.property);
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
286
|
+
} else if (node.type === 'AssignmentExpression' || node.type === 'AssignmentPattern') {
|
|
287
|
+
// Visit in source order: left, right
|
|
288
|
+
if (node.left) {
|
|
289
|
+
visit(node.left);
|
|
290
|
+
}
|
|
291
|
+
if (node.right) {
|
|
292
|
+
visit(node.right);
|
|
293
|
+
}
|
|
294
|
+
return;
|
|
295
|
+
} else if (node.type === 'ObjectExpression' || node.type === 'ObjectPattern') {
|
|
296
|
+
// Visit properties in order
|
|
297
|
+
if (node.properties) {
|
|
298
|
+
for (const prop of node.properties) {
|
|
299
|
+
visit(prop);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
303
|
+
} else if (node.type === 'Property') {
|
|
304
|
+
// Visit in source order: key, value
|
|
305
|
+
if (node.key) {
|
|
306
|
+
visit(node.key);
|
|
307
|
+
}
|
|
308
|
+
if (node.value) {
|
|
309
|
+
visit(node.value);
|
|
310
|
+
}
|
|
311
|
+
return;
|
|
312
|
+
} else if (node.type === 'ArrayExpression' || node.type === 'ArrayPattern') {
|
|
313
|
+
// Visit elements in order
|
|
314
|
+
if (node.elements) {
|
|
315
|
+
for (const element of node.elements) {
|
|
316
|
+
if (element) visit(element);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return;
|
|
320
|
+
} else if (node.type === 'ConditionalExpression') {
|
|
321
|
+
// Visit in source order: test, consequent, alternate
|
|
322
|
+
if (node.test) {
|
|
323
|
+
visit(node.test);
|
|
324
|
+
}
|
|
325
|
+
if (node.consequent) {
|
|
326
|
+
visit(node.consequent);
|
|
327
|
+
}
|
|
328
|
+
if (node.alternate) {
|
|
329
|
+
visit(node.alternate);
|
|
330
|
+
}
|
|
331
|
+
return;
|
|
332
|
+
} else if (node.type === 'UnaryExpression' || node.type === 'UpdateExpression') {
|
|
333
|
+
// Visit argument
|
|
334
|
+
if (node.argument) {
|
|
335
|
+
visit(node.argument);
|
|
336
|
+
}
|
|
337
|
+
return;
|
|
338
|
+
} else if (node.type === 'TemplateLiteral') {
|
|
339
|
+
// Visit quasis and expressions in order
|
|
340
|
+
for (let i = 0; i < node.quasis.length; i++) {
|
|
341
|
+
if (node.quasis[i]) {
|
|
342
|
+
visit(node.quasis[i]);
|
|
343
|
+
}
|
|
344
|
+
if (i < node.expressions.length && node.expressions[i]) {
|
|
345
|
+
visit(node.expressions[i]);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
} else if (node.type === 'TaggedTemplateExpression') {
|
|
350
|
+
// Visit in source order: tag, quasi
|
|
351
|
+
if (node.tag) {
|
|
352
|
+
visit(node.tag);
|
|
353
|
+
}
|
|
354
|
+
if (node.quasi) {
|
|
355
|
+
visit(node.quasi);
|
|
356
|
+
}
|
|
357
|
+
return;
|
|
358
|
+
} else if (node.type === 'ReturnStatement' || node.type === 'ThrowStatement') {
|
|
359
|
+
// Visit argument
|
|
360
|
+
if (node.argument) {
|
|
361
|
+
visit(node.argument);
|
|
362
|
+
}
|
|
363
|
+
return;
|
|
364
|
+
} else if (node.type === 'ExpressionStatement') {
|
|
365
|
+
// Visit expression
|
|
366
|
+
if (node.expression) {
|
|
367
|
+
visit(node.expression);
|
|
368
|
+
}
|
|
369
|
+
return;
|
|
370
|
+
} else if (node.type === 'BlockStatement' || node.type === 'Program') {
|
|
371
|
+
// Visit body statements in order
|
|
372
|
+
if (node.body) {
|
|
373
|
+
for (const statement of node.body) {
|
|
374
|
+
visit(statement);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return;
|
|
378
|
+
} else if (node.type === 'SwitchStatement') {
|
|
379
|
+
// Visit in source order: discriminant, cases
|
|
380
|
+
if (node.discriminant) {
|
|
381
|
+
visit(node.discriminant);
|
|
382
|
+
}
|
|
383
|
+
if (node.cases) {
|
|
384
|
+
for (const caseNode of node.cases) {
|
|
385
|
+
visit(caseNode);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return;
|
|
389
|
+
} else if (node.type === 'SwitchCase') {
|
|
390
|
+
// Visit in source order: test, consequent
|
|
391
|
+
if (node.test) {
|
|
392
|
+
visit(node.test);
|
|
393
|
+
}
|
|
394
|
+
if (node.consequent) {
|
|
395
|
+
for (const statement of node.consequent) {
|
|
396
|
+
visit(statement);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return;
|
|
400
|
+
} else if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {
|
|
401
|
+
// Visit in source order: id, superClass, body
|
|
402
|
+
if (node.id) {
|
|
403
|
+
visit(node.id);
|
|
404
|
+
}
|
|
405
|
+
if (node.superClass) {
|
|
406
|
+
visit(node.superClass);
|
|
407
|
+
}
|
|
408
|
+
if (node.body) {
|
|
409
|
+
visit(node.body);
|
|
410
|
+
}
|
|
411
|
+
return;
|
|
412
|
+
} else if (node.type === 'ClassBody') {
|
|
413
|
+
// Visit body in order
|
|
414
|
+
if (node.body) {
|
|
415
|
+
for (const member of node.body) {
|
|
416
|
+
visit(member);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return;
|
|
420
|
+
} else if (node.type === 'MethodDefinition') {
|
|
421
|
+
// Visit in source order: key, value
|
|
422
|
+
if (node.key) {
|
|
423
|
+
visit(node.key);
|
|
424
|
+
}
|
|
425
|
+
if (node.value) {
|
|
426
|
+
visit(node.value);
|
|
427
|
+
}
|
|
428
|
+
return;
|
|
251
429
|
}
|
|
430
|
+
|
|
431
|
+
next();
|
|
252
432
|
}
|
|
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
|
-
}
|
|
433
|
+
});
|
|
298
434
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
435
|
+
// Process each token in order
|
|
436
|
+
for (const text of tokens) {
|
|
437
|
+
const sourcePos = findInSource(text);
|
|
438
|
+
const genPos = findInGenerated(text);
|
|
439
|
+
|
|
440
|
+
if (sourcePos !== null && genPos !== null) {
|
|
441
|
+
mappings.push({
|
|
442
|
+
sourceOffsets: [sourcePos],
|
|
443
|
+
generatedOffsets: [genPos],
|
|
444
|
+
lengths: [text.length],
|
|
445
|
+
data: mapping_data,
|
|
446
|
+
});
|
|
303
447
|
}
|
|
304
448
|
}
|
|
305
449
|
|
|
306
|
-
// Sort mappings by source offset
|
|
450
|
+
// Sort mappings by source offset
|
|
307
451
|
mappings.sort((a, b) => a.sourceOffsets[0] - b.sourceOffsets[0]);
|
|
308
452
|
|
|
309
453
|
return {
|
|
310
454
|
code: generated_code,
|
|
311
455
|
mappings,
|
|
312
456
|
};
|
|
313
|
-
}
|
|
457
|
+
}
|