ripple 0.3.13 → 0.3.15

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/package.json +5 -30
  3. package/src/runtime/array.js +38 -38
  4. package/src/runtime/create-subscriber.js +2 -2
  5. package/src/runtime/internal/client/bindings.js +4 -6
  6. package/src/runtime/internal/client/events.js +8 -3
  7. package/src/runtime/internal/client/hmr.js +5 -17
  8. package/src/runtime/internal/client/runtime.js +1 -0
  9. package/src/runtime/internal/server/blocks.js +7 -9
  10. package/src/runtime/internal/server/index.js +14 -22
  11. package/src/runtime/media-query.js +34 -33
  12. package/src/runtime/object.js +7 -10
  13. package/src/runtime/proxy.js +2 -3
  14. package/src/runtime/reactive-value.js +23 -21
  15. package/src/utils/ast.js +1 -1
  16. package/src/utils/attributes.js +43 -0
  17. package/src/utils/builders.js +2 -2
  18. package/tests/client/basic/basic.components.test.rsrx +103 -1
  19. package/tests/client/basic/basic.errors.test.rsrx +1 -1
  20. package/tests/client/basic/basic.styling.test.rsrx +1 -1
  21. package/tests/client/compiler/compiler.assignments.test.rsrx +1 -1
  22. package/tests/client/compiler/compiler.attributes.test.rsrx +1 -1
  23. package/tests/client/compiler/compiler.basic.test.rsrx +51 -14
  24. package/tests/client/compiler/compiler.tracked-access.test.rsrx +1 -1
  25. package/tests/client/compiler/compiler.try-in-function.test.rsrx +1 -1
  26. package/tests/client/compiler/compiler.typescript.test.rsrx +1 -1
  27. package/tests/client/css/global-additional-cases.test.rsrx +1 -1
  28. package/tests/client/css/global-advanced-selectors.test.rsrx +1 -1
  29. package/tests/client/css/global-at-rules.test.rsrx +1 -1
  30. package/tests/client/css/global-basic.test.rsrx +1 -1
  31. package/tests/client/css/global-classes-ids.test.rsrx +1 -1
  32. package/tests/client/css/global-combinators.test.rsrx +1 -1
  33. package/tests/client/css/global-complex-nesting.test.rsrx +1 -1
  34. package/tests/client/css/global-edge-cases.test.rsrx +1 -1
  35. package/tests/client/css/global-keyframes.test.rsrx +1 -1
  36. package/tests/client/css/global-nested.test.rsrx +1 -1
  37. package/tests/client/css/global-pseudo.test.rsrx +1 -1
  38. package/tests/client/css/global-scoping.test.rsrx +1 -1
  39. package/tests/client/css/style-identifier.test.rsrx +1 -1
  40. package/tests/client/return.test.rsrx +1 -1
  41. package/tests/hydration/build-components.js +1 -1
  42. package/tests/server/basic.components.test.rsrx +114 -0
  43. package/tests/server/compiler.test.rsrx +38 -1
  44. package/tests/server/style-identifier.test.rsrx +1 -1
  45. package/tests/setup-server.js +1 -1
  46. package/tests/utils/compiler-compat-config.test.js +1 -1
  47. package/types/index.d.ts +1 -1
  48. package/src/compiler/comment-utils.js +0 -91
  49. package/src/compiler/errors.js +0 -77
  50. package/src/compiler/identifier-utils.js +0 -80
  51. package/src/compiler/index.d.ts +0 -127
  52. package/src/compiler/index.js +0 -89
  53. package/src/compiler/phases/1-parse/index.js +0 -3007
  54. package/src/compiler/phases/1-parse/style.js +0 -704
  55. package/src/compiler/phases/2-analyze/css-analyze.js +0 -160
  56. package/src/compiler/phases/2-analyze/index.js +0 -2208
  57. package/src/compiler/phases/2-analyze/prune.js +0 -1131
  58. package/src/compiler/phases/2-analyze/validation.js +0 -168
  59. package/src/compiler/phases/3-transform/client/index.js +0 -5264
  60. package/src/compiler/phases/3-transform/segments.js +0 -2125
  61. package/src/compiler/phases/3-transform/server/index.js +0 -1749
  62. package/src/compiler/phases/3-transform/stylesheet.js +0 -545
  63. package/src/compiler/scope.js +0 -476
  64. package/src/compiler/source-map-utils.js +0 -358
  65. package/src/compiler/types/acorn.d.ts +0 -11
  66. package/src/compiler/types/estree-jsx.d.ts +0 -11
  67. package/src/compiler/types/estree.d.ts +0 -11
  68. package/src/compiler/types/index.d.ts +0 -1411
  69. package/src/compiler/types/parse.d.ts +0 -1723
  70. package/src/compiler/utils.js +0 -1258
@@ -1,2125 +0,0 @@
1
- /**
2
- @import * as AST from 'estree';
3
- @import * as ESTreeJSX from 'estree-jsx';
4
- @import { DocumentHighlightKind } from 'vscode-languageserver-types';
5
- @import { RawSourceMap } from 'source-map';
6
- @import {
7
- CustomMappingData,
8
- PluginActionOverrides,
9
- CodeMapping,
10
- VolarMappingsResult,
11
- } from 'ripple/compiler';
12
- @import { PostProcessingChanges } from './client/index.js';
13
- @import { CodeMapping as VolarCodeMapping } from '@volar/language-core';
14
- */
15
-
16
- /**
17
- @typedef {{
18
- start: number,
19
- end: number,
20
- content: string,
21
- id: string,
22
- }} CssSourceRegion;
23
- @typedef {{
24
- source: string | null | undefined;
25
- generated: string;
26
- loc: AST.SourceLocation;
27
- metadata: PluginActionOverrides;
28
- end_loc?: AST.SourceLocation;
29
- mappingData?: Partial<VolarCodeMapping['data']>;
30
- }} Token;
31
- @typedef {{
32
- name: string,
33
- line: number,
34
- column: number,
35
- offset: number,
36
- length: number,
37
- sourceOffset: number,
38
- }} TokenClass
39
- @typedef {Map<string, AST.Element['metadata']['css']>} CssElementInfo
40
- */
41
-
42
- import { walk } from 'zimmerframe';
43
- import {
44
- build_src_to_gen_map,
45
- get_generated_position,
46
- offset_to_line_col,
47
- loc_to_offset,
48
- mapping_data,
49
- mapping_data_verify_only,
50
- mapping_data_verify_complete,
51
- build_line_offsets,
52
- get_mapping_from_node,
53
- } from '../../source-map-utils.js';
54
-
55
- const LABEL_TO_COMPONENT_REPLACE_REGEX = /(function|\((property|method)\))/;
56
-
57
- /**
58
- * @param {string} content
59
- * @returns {string}
60
- */
61
- function replace_label_to_component(content) {
62
- return content.replace(LABEL_TO_COMPONENT_REPLACE_REGEX, (_, fn, kind) => {
63
- if (fn === 'function') return 'component';
64
- return `(component ${kind})`;
65
- });
66
- }
67
-
68
- /**
69
- * @param {string} [hash]
70
- * @param {string} [fallback]
71
- * @returns `style-${hash | fallback}`
72
- */
73
- function get_style_region_id(hash, fallback) {
74
- return `style-${hash || fallback}`;
75
- }
76
-
77
- /**
78
- * Extract CSS source regions from style elements in the AST
79
- * @param {AST.Node} ast - The parsed AST
80
- * @param {number[]} src_line_offsets
81
- * @param {{
82
- * regions: CssSourceRegion[],
83
- * css_element_info: CssElementInfo,
84
- * }} param2
85
- * @returns {void}
86
- */
87
- function visit_source_ast(ast, src_line_offsets, { regions, css_element_info }) {
88
- let region_id = 0;
89
- walk(ast, null, {
90
- Element(node, context) {
91
- // Check if this is a style element with CSS content
92
- if (node.id?.type === 'Identifier' && node.id?.name === 'style' && node.css) {
93
- const openLoc = /** @type {ESTreeJSX.JSXOpeningElement & AST.NodeWithLocation} */ (
94
- node.openingElement
95
- ).loc;
96
- const cssStart = loc_to_offset(openLoc.end.line, openLoc.end.column, src_line_offsets);
97
-
98
- const closeLoc = /** @type {ESTreeJSX.JSXClosingElement & AST.NodeWithLocation} */ (
99
- node.closingElement
100
- ).loc;
101
- const cssEnd = loc_to_offset(closeLoc.start.line, closeLoc.start.column, src_line_offsets);
102
-
103
- regions.push({
104
- start: cssStart,
105
- end: cssEnd,
106
- content: node.css,
107
- id: get_style_region_id(node.metadata.styleScopeHash, `head-${region_id}`),
108
- });
109
- }
110
-
111
- context.next();
112
- },
113
- Attribute(node, context) {
114
- const element = context.path?.find((n) => n.type === 'Element');
115
- if (element?.metadata?.css?.scopedClasses) {
116
- // we don't need to check is_element_dom_element(node)
117
- // since scopedClasses are added during pruning only to DOM elements
118
- const css = element.metadata.css;
119
- const { line, column } = node.value?.loc?.start ?? {};
120
-
121
- if (line === undefined || column === undefined) {
122
- return;
123
- }
124
-
125
- css_element_info.set(`${line}:${column}`, css);
126
- }
127
- },
128
- });
129
- }
130
-
131
- /**
132
- * Extract individual class names and their offsets from class attribute values
133
- * Handles: "foo bar", { foo: true }, ['foo', { bar: true }], etc.
134
- *
135
- * @param {AST.Node} node - The attribute value node
136
- * @param {ReturnType<typeof build_src_to_gen_map>[0]} src_to_gen_map
137
- * @param {number[]} gen_line_offsets
138
- * @param {number[]} src_line_offsets
139
- * @returns {TokenClass[]}
140
- */
141
- function extract_classes(node, src_to_gen_map, gen_line_offsets, src_line_offsets) {
142
- /** @type {TokenClass[]} */
143
- const classes = [];
144
-
145
- switch (node.type) {
146
- case 'Literal': {
147
- // Static: class="foo bar baz"
148
-
149
- const content = node.raw ?? '';
150
- let text = content;
151
- let textOffset = 0;
152
-
153
- // Remove quotes
154
- if (
155
- (content.startsWith(`'`) && content.endsWith(`'`)) ||
156
- (content.startsWith(`"`) && content.endsWith(`"`)) ||
157
- (content.startsWith('`') && content.endsWith('`'))
158
- ) {
159
- text = content.slice(1, -1);
160
- textOffset = 1;
161
- }
162
-
163
- // Split by whitespace
164
- const classNames = text.split(/\s+/).filter((c) => c.length > 0);
165
- const nodeSrcStart = /** @type {AST.Position} */ (node.loc?.start);
166
-
167
- let currentPos = 0;
168
- const nodeGenStart = get_generated_position(
169
- nodeSrcStart.line,
170
- nodeSrcStart.column,
171
- src_to_gen_map,
172
- );
173
- const offset = loc_to_offset(nodeGenStart.line, nodeGenStart.column, gen_line_offsets);
174
- const sourceOffset = loc_to_offset(nodeSrcStart.line, nodeSrcStart.column, src_line_offsets);
175
-
176
- for (const name of classNames) {
177
- const classStart = text.indexOf(name, currentPos);
178
- const classOffset = offset + textOffset + classStart;
179
- const classSourceOffset = sourceOffset + textOffset + classStart;
180
- const { line, column } = offset_to_line_col(classOffset, gen_line_offsets);
181
-
182
- classes.push({
183
- name,
184
- line,
185
- column,
186
- offset: classOffset,
187
- length: name.length,
188
- sourceOffset: classSourceOffset,
189
- });
190
-
191
- currentPos = classStart + name.length;
192
- }
193
- break;
194
- }
195
-
196
- case 'ObjectExpression': {
197
- // Dynamic: class={{ foo: true, bar: @show }}
198
- for (const prop of node.properties) {
199
- if (prop.type === 'Property' && prop.key) {
200
- const key = prop.key;
201
- if (key.type === 'Identifier' && key.name && key.loc) {
202
- const nodeSrcStart = /** @type {AST.Position} */ (key.loc?.start);
203
- const nodeGenStart = get_generated_position(
204
- nodeSrcStart.line,
205
- nodeSrcStart.column,
206
- src_to_gen_map,
207
- );
208
- const offset = loc_to_offset(nodeGenStart.line, nodeGenStart.column, gen_line_offsets);
209
- const sourceOffset = loc_to_offset(
210
- nodeSrcStart.line,
211
- nodeSrcStart.column,
212
- src_line_offsets,
213
- );
214
- const { line, column } = offset_to_line_col(offset, gen_line_offsets);
215
-
216
- classes.push({
217
- name: key.name,
218
- line,
219
- column,
220
- offset,
221
- length: key.name.length,
222
- sourceOffset,
223
- });
224
- }
225
- }
226
- }
227
- break;
228
- }
229
-
230
- case 'ArrayExpression': {
231
- // Dynamic: class={['foo', { bar: true }]}
232
- for (const el of node.elements) {
233
- if (el) {
234
- classes.push(...extract_classes(el, src_to_gen_map, gen_line_offsets, src_line_offsets));
235
- }
236
- }
237
- break;
238
- }
239
-
240
- case 'ConditionalExpression': {
241
- // Conditional: class={@show ? 'active' : 'inactive'}
242
- if (node.consequent) {
243
- classes.push(
244
- ...extract_classes(node.consequent, src_to_gen_map, gen_line_offsets, src_line_offsets),
245
- );
246
- }
247
- if (node.alternate) {
248
- classes.push(
249
- ...extract_classes(node.alternate, src_to_gen_map, gen_line_offsets, src_line_offsets),
250
- );
251
- }
252
- break;
253
- }
254
-
255
- case 'LogicalExpression': {
256
- // Logical: class={[@show && 'active']}
257
- if (node.operator === '&&' && node.right) {
258
- classes.push(
259
- ...extract_classes(node.right, src_to_gen_map, gen_line_offsets, src_line_offsets),
260
- );
261
- } else if (node.operator === '||') {
262
- if (node.left) {
263
- classes.push(
264
- ...extract_classes(node.left, src_to_gen_map, gen_line_offsets, src_line_offsets),
265
- );
266
- }
267
- if (node.right) {
268
- classes.push(
269
- ...extract_classes(node.right, src_to_gen_map, gen_line_offsets, src_line_offsets),
270
- );
271
- }
272
- }
273
- break;
274
- }
275
- }
276
-
277
- return classes;
278
- }
279
-
280
- /**
281
- * Create Volar mappings by walking the transformed AST
282
- * @param {AST.Node} ast - The transformed AST
283
- * @param {AST.Node} ast_from_source - The original AST from source
284
- * @param {string} source - Original source code
285
- * @param {string} generated_code - Generated code (returned in output, not used for searching)
286
- * @param {RawSourceMap} source_map - Esrap source map for accurate position lookup
287
- * @param {PostProcessingChanges } post_processing_changes - Optional post-processing changes
288
- * @param {number[]} line_offsets - Pre-computed line offsets array for generated code
289
- * @returns {Omit<VolarMappingsResult, 'errors'>}
290
- */
291
- export function convert_source_map_to_mappings(
292
- ast,
293
- ast_from_source,
294
- source,
295
- generated_code,
296
- source_map,
297
- post_processing_changes,
298
- line_offsets,
299
- ) {
300
- /** @type {CodeMapping[]} */
301
- const mappings = [];
302
- let isImportDeclarationPresent = false;
303
-
304
- const src_line_offsets = build_line_offsets(source);
305
- const gen_line_offsets = build_line_offsets(generated_code);
306
-
307
- const [src_to_gen_map] = build_src_to_gen_map(
308
- source_map,
309
- post_processing_changes,
310
- line_offsets,
311
- generated_code,
312
- );
313
-
314
- /** @type {Token[]} */
315
- const tokens = [];
316
- /** @type {CssSourceRegion[]} */
317
- const css_regions = [];
318
- /** @type {CssElementInfo} */
319
- const css_element_info = new Map();
320
-
321
- visit_source_ast(ast_from_source, src_line_offsets, {
322
- regions: css_regions,
323
- css_element_info,
324
- });
325
-
326
- /**
327
- * Needed for a mapping that includes the computed brackets for diagnostics
328
- * @param {AST.MethodDefinition | AST.Property} node
329
- * @param {CodeMapping[]} mappings
330
- * @returns {void}
331
- */
332
- function set_bracket_computed_mapping(node, mappings) {
333
- if (node.loc) {
334
- const key = /** @type {typeof node.key & AST.NodeWithLocation} */ (node.key);
335
- mappings.push(
336
- get_mapping_from_node(
337
- /** @type {AST.NodeWithLocation} */ ({
338
- start: key.start - 1,
339
- end: key.end + 1,
340
- loc: {
341
- start: { line: key.loc.start.line, column: key.loc.start.column - 1 },
342
- end: { line: key.loc.end.line, column: key.loc.end.column + 1 },
343
- },
344
- }),
345
- src_to_gen_map,
346
- gen_line_offsets,
347
- mapping_data_verify_only,
348
- ),
349
- );
350
- }
351
- }
352
-
353
- /**
354
- * @typedef {AST.MethodDefinition & {value: {metadata: {is_component: true}}}} MethodIsComponent
355
- * @typedef {AST.Property & {value: AST.FunctionExpression, method: true} & {value: {metadata: {is_component: true}}}} PropertyIsComponent
356
- */
357
-
358
- /**
359
- * Maps `component` to the identifier's location
360
- * e.g. const obj = { component something() { } }
361
- * since there is no function keyword in source maps
362
- * @param {MethodIsComponent | PropertyIsComponent} node
363
- * @returns {void}
364
- */
365
- function set_component_mapping_to_name(node) {
366
- if (node.key.loc) {
367
- /** @type {CodeMapping} */
368
- let mapping;
369
- let start = /** @type {AST.NodeWithLocation} */ (node).start;
370
- let length = 'component'.length;
371
-
372
- if (node.value.type === 'FunctionExpression' && node.value.id) {
373
- const id = /** @type {AST.Identifier & AST.NodeWithLocation} */ (node.value.id);
374
- mapping = get_mapping_from_node(id, src_to_gen_map, gen_line_offsets);
375
- } else {
376
- // e.g. key is computed or literal
377
- mapping = get_mapping_from_node(node.key, src_to_gen_map, gen_line_offsets);
378
- }
379
-
380
- // overwrite source start and length to point to 'component' keyword
381
- mapping.sourceOffsets = [start];
382
- mapping.lengths = [length];
383
- mapping.data.customData.hover = replace_label_to_component;
384
-
385
- mappings.push(mapping);
386
- }
387
- }
388
-
389
- /**
390
- * @param {AST.Literal} node
391
- * @param {boolean} [is_component]
392
- */
393
- function handle_literal(node, is_component = false) {
394
- if (node.loc) {
395
- const mapping = get_mapping_from_node(node, src_to_gen_map, gen_line_offsets);
396
-
397
- if (is_component) {
398
- mapping.data.customData.hover = replace_label_to_component;
399
- }
400
-
401
- mappings.push(mapping);
402
- }
403
- }
404
-
405
- // We have to visit everything in generated order to maintain correct indices
406
-
407
- walk(ast, null, {
408
- _(node, { visit }) {
409
- // Collect key node types: Identifiers, Literals, and JSX Elements
410
- if (node.type === 'Identifier') {
411
- // Only create mappings for identifiers with location info (from source)
412
- // Synthesized identifiers (created by builders) don't have .loc and are skipped
413
- if (node.name && node.loc) {
414
- /** @type {Token} */
415
- let token;
416
- // Check if this identifier was changed in metadata (e.g., #Map -> RippleMap)
417
- // Or if it was capitalized during transformation
418
- if (node.metadata?.source_name) {
419
- token = {
420
- source: node.metadata.source_name,
421
- generated: node.name,
422
- loc: node.loc,
423
- metadata: {},
424
- };
425
- } else {
426
- token = {
427
- source: node.name,
428
- generated: node.name,
429
- loc: node.loc,
430
- metadata: {},
431
- };
432
- }
433
-
434
- if (node.metadata?.is_component) {
435
- // only if the node has a component as the parent
436
- token.metadata.hover = replace_label_to_component;
437
- }
438
- tokens.push(token);
439
- }
440
- return; // Leaf node, don't traverse further
441
- } else if (node.type === 'JSXIdentifier') {
442
- // JSXIdentifiers can also be capitalized (for dynamic components)
443
- if (node.loc && node.name) {
444
- if (node.metadata?.is_capitalized) {
445
- tokens.push({
446
- source: node.metadata.source_name,
447
- generated: node.name,
448
- loc: node.loc,
449
- metadata: {},
450
- });
451
- } else {
452
- tokens.push({ source: node.name, generated: node.name, loc: node.loc, metadata: {} });
453
- }
454
- }
455
- return; // Leaf node, don't traverse further
456
- } else if (node.type === 'Literal') {
457
- handle_literal(node);
458
- return; // Leaf node, don't traverse further
459
- } else if (node.type === 'ImportDeclaration') {
460
- isImportDeclarationPresent = true;
461
-
462
- // Add 'import' keyword token to anchor statement-level diagnostics
463
- // And the last character of the statement (semicolon or closing brace)
464
- // (e.g., when ALL imports are unused, TS reports on the whole statement)
465
- // We only map the 'import' and the last character
466
- // to avoid overlapping with individual specifier mappings
467
- // which would interfere when only SOME imports are unused.
468
- if (node.loc) {
469
- tokens.push({
470
- source: 'import',
471
- generated: 'import',
472
- loc: {
473
- start: node.loc.start,
474
- end: {
475
- line: node.loc.start.line,
476
- column: node.loc.start.column + 'import'.length,
477
- },
478
- },
479
- metadata: {},
480
- });
481
-
482
- tokens.push({
483
- source:
484
- source[loc_to_offset(node.loc.end.line, node.loc.end.column - 1, src_line_offsets)],
485
- // we always add `;' in the generated import
486
- generated: ';',
487
- loc: {
488
- start: {
489
- line: node.loc.end.line,
490
- column: node.loc.end.column - 1,
491
- },
492
- end: node.loc.end,
493
- },
494
- metadata: {},
495
- });
496
- }
497
-
498
- // Visit specifiers in source order
499
- if (node.specifiers) {
500
- for (const specifier of node.specifiers) {
501
- visit(specifier);
502
- }
503
- }
504
- visit(node.source);
505
- return;
506
- } else if (node.type === 'ImportSpecifier') {
507
- // If local and imported are the same, only visit local to avoid duplicates
508
- // Otherwise visit both in order
509
- if (
510
- node.imported &&
511
- node.local &&
512
- /** @type {AST.Identifier} */ (node.imported).name !== node.local.name
513
- ) {
514
- visit(node.imported);
515
- visit(node.local);
516
- } else if (node.local) {
517
- visit(node.local);
518
- }
519
- return;
520
- } else if (
521
- node.type === 'ImportDefaultSpecifier' ||
522
- node.type === 'ImportNamespaceSpecifier'
523
- ) {
524
- // Just visit local
525
- if (node.local) {
526
- visit(node.local);
527
- }
528
- return;
529
- } else if (node.type === 'ExportSpecifier') {
530
- // If local and exported are the same, only visit local to avoid duplicates
531
- // Otherwise visit both in order
532
- if (
533
- node.local &&
534
- node.exported &&
535
- /** @type {AST.Identifier} */ (node.local).name !==
536
- /** @type {AST.Identifier} */ (node.exported).name
537
- ) {
538
- visit(node.local);
539
- visit(node.exported);
540
- } else if (node.local) {
541
- visit(node.local);
542
- }
543
- return;
544
- } else if (node.type === 'ExportNamedDeclaration') {
545
- if (node.specifiers && node.specifiers.length > 0) {
546
- for (const specifier of node.specifiers) {
547
- visit(specifier);
548
- }
549
- }
550
- if (node.declaration) {
551
- // The declaration will be visited with proper ordering
552
- visit(node.declaration);
553
- }
554
- return;
555
- } else if (node.type === 'ExportDefaultDeclaration') {
556
- // Visit the declaration
557
- if (node.declaration) {
558
- visit(/** @type {AST.Node} */ (node.declaration));
559
- }
560
- return;
561
- } else if (node.type === 'ExportAllDeclaration') {
562
- // Nothing to visit (just source string)
563
- return;
564
- } else if (node.type === 'JSXOpeningElement') {
565
- // Visit name and attributes in source order
566
- visit(node.name);
567
- for (const attr of node.attributes) {
568
- visit(attr);
569
- }
570
- return;
571
- } else if (node.type === 'JSXClosingElement') {
572
- visit(node.name);
573
- return;
574
- } else if (node.type === 'JSXAttribute') {
575
- // Visit name and value in source order
576
- // For shorthand attributes ({ count }), key and value are the same node, only visit once
577
- if (node.shorthand) {
578
- if (node.value) {
579
- visit(node.value);
580
- }
581
- } else {
582
- const attr =
583
- node.name.name === 'class' && node.value?.type === 'JSXExpressionContainer'
584
- ? node.value.expression
585
- : node.value;
586
-
587
- const css = attr
588
- ? css_element_info.get(`${attr.loc?.start.line}:${attr.loc?.start.column}`)
589
- : null;
590
-
591
- if (attr && css) {
592
- // Extract class names from the attribute value
593
- const classes = extract_classes(
594
- attr,
595
- src_to_gen_map,
596
- gen_line_offsets,
597
- src_line_offsets,
598
- );
599
-
600
- // For each class name, look up CSS location and create token
601
- for (const { name, line, column, offset, sourceOffset, length } of classes) {
602
- const cssLocation = css.scopedClasses.get(name);
603
-
604
- if (!cssLocation) {
605
- continue;
606
- }
607
-
608
- mappings.push({
609
- sourceOffsets: [sourceOffset],
610
- generatedOffsets: [offset],
611
- lengths: [length],
612
- generatedLengths: [length],
613
- data: {
614
- ...mapping_data,
615
- customData: {
616
- hover:
617
- '```css\n.' +
618
- name +
619
- '\n```\n\nCSS class selector.\n\nUse **Cmd+Click** (macOS) or **Ctrl+Click** (Windows/Linux) to navigate to its definition.',
620
- definition: {
621
- description: `CSS class selector for '.${name}'`,
622
- location: {
623
- embeddedId: get_style_region_id(css.hash),
624
- start: cssLocation.start,
625
- end: cssLocation.end,
626
- },
627
- },
628
- },
629
- },
630
- });
631
- }
632
- } else {
633
- if (node.name) {
634
- visit(node.name);
635
- }
636
-
637
- if (
638
- node.name.type === 'JSXIdentifier' &&
639
- node.name.metadata?.is_component &&
640
- node.name.loc
641
- ) {
642
- const mapping = get_mapping_from_node(
643
- node.name,
644
- src_to_gen_map,
645
- gen_line_offsets,
646
- mapping_data,
647
- );
648
- mapping.sourceOffsets = [
649
- /** @type {AST.NodeWithLocation} */ (node.name).start - 'component '.length,
650
- ];
651
- mapping.lengths = ['component'.length];
652
-
653
- mapping.data.customData.hover = replace_label_to_component;
654
- mappings.push(mapping);
655
- }
656
- if (node.value) {
657
- visit(node.value);
658
- }
659
- }
660
- }
661
- return;
662
- } else if (node.type === 'JSXSpreadAttribute') {
663
- // Visit the spread argument
664
- if (node.argument) {
665
- visit(node.argument);
666
- }
667
- return;
668
- } else if (node.type === 'JSXExpressionContainer') {
669
- if (node.loc) {
670
- mappings.push(
671
- get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
672
- );
673
- }
674
- // Visit the expression inside {}
675
- if (node.expression) {
676
- visit(node.expression);
677
- }
678
- return;
679
- } else if (node.type === 'JSXText') {
680
- // Text content, no tokens to collect
681
- return;
682
- } else if (node.type === 'JSXElement') {
683
- // Manually visit in source order: opening element, children, closing element
684
-
685
- // 1. Visit opening element (name and attributes)
686
- // Add tokens for '<' and '>' brackets to ensure auto-close feature works
687
- const opening = node.openingElement;
688
- const closing = node.closingElement;
689
-
690
- if (opening.loc) {
691
- // Add tokens for '<' and '>' brackets to ensure auto-close feature works
692
- tokens.push({
693
- source: '<',
694
- generated: '<',
695
- loc: {
696
- start: { line: opening.loc.start.line, column: opening.loc.start.column },
697
- end: { line: opening.loc.start.line, column: opening.loc.start.column + 1 },
698
- },
699
- metadata: {},
700
- mappingData: mapping_data_verify_only,
701
- });
702
-
703
- if (!opening.selfClosing) {
704
- tokens.push({
705
- source: '>',
706
- generated: '>',
707
- loc: {
708
- start: { line: opening.loc.end.line, column: opening.loc.end.column - 1 },
709
- end: { line: opening.loc.end.line, column: opening.loc.end.column },
710
- },
711
- metadata: {},
712
- // we need the completion only on the closing tag `>`
713
- // to cause the closing tag to be auto-added
714
- mappingData: mapping_data_verify_complete,
715
- });
716
- }
717
- }
718
-
719
- visit(opening);
720
-
721
- // 2. Visit children in order
722
- if (node.children) {
723
- for (const child of node.children) {
724
- visit(/** @type {AST.Node} */ (child));
725
- }
726
- }
727
-
728
- if (closing || opening.selfClosing) {
729
- // Add the whole closing tag or the self-closing
730
- const mapping = get_mapping_from_node(
731
- closing ? closing : opening,
732
- src_to_gen_map,
733
- gen_line_offsets,
734
- mapping_data_verify_only,
735
- );
736
-
737
- // The generated code includes a semicolon after the closing or self-closed tag
738
- // We're extending the mapping to include the semicolon
739
- // because the diagnostics errors can include the whole element
740
- // and we need to account for the semicolon as it's a part of the diagnostic
741
- // At the same time, we could've instead applied this logic to the whole `node` element
742
- // but since we already map the opening - start, we just need the proper end
743
- // and it was causing some issues with mappings
744
- mapping.generatedLengths = [mapping.generatedLengths[0] + 1];
745
- mappings.push(mapping);
746
- }
747
-
748
- if (closing) {
749
- visit(closing);
750
- }
751
-
752
- return;
753
- } else if (
754
- node.type === 'FunctionDeclaration' ||
755
- node.type === 'FunctionExpression' ||
756
- node.type === 'ArrowFunctionExpression'
757
- ) {
758
- const is_method = node.metadata?.is_method;
759
- // Add function/component keyword token
760
- if (
761
- (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') &&
762
- !is_method
763
- ) {
764
- const node_fn = /** @type (typeof node) & AST.NodeWithLocation */ (node);
765
- const is_component = node_fn.metadata?.is_component;
766
- const source_func_keyword = is_component ? 'component' : 'function';
767
- let start_col = node_fn.loc.start.column;
768
- let start = node_fn.start;
769
- const async_keyword = 'async';
770
-
771
- if (node_fn.async) {
772
- // We explicitly mapped async and function in esrap
773
- tokens.push({
774
- source: async_keyword,
775
- generated: async_keyword,
776
- loc: {
777
- start: { line: node_fn.loc.start.line, column: start_col },
778
- end: {
779
- line: node_fn.loc.start.line,
780
- column: start_col + async_keyword.length,
781
- },
782
- },
783
- metadata: {},
784
- });
785
-
786
- start_col += async_keyword.length + 1; // +1 for space
787
- start += async_keyword.length + 1;
788
- }
789
-
790
- tokens.push({
791
- source: source_func_keyword,
792
- generated: 'function',
793
- loc: {
794
- start: { line: node_fn.loc.start.line, column: start_col },
795
- end: {
796
- line: node_fn.loc.start.line,
797
- column: start_col + source_func_keyword.length,
798
- },
799
- },
800
- metadata: is_component ? { hover: replace_label_to_component } : {},
801
- });
802
- }
803
-
804
- // Visit in source order: id, params, body
805
- // If it's a part of a method, skip visiting id
806
- // as the name was already covered by the key in MethodDefinition or Property
807
- if (
808
- /** @type {AST.FunctionDeclaration | AST.FunctionExpression} */ (node).id &&
809
- !is_method
810
- ) {
811
- visit(/** @type {AST.FunctionDeclaration | AST.FunctionExpression} */ (node).id);
812
- }
813
-
814
- if (node.typeParameters) {
815
- visit(node.typeParameters);
816
- }
817
-
818
- if (node.params) {
819
- for (const param of node.params) {
820
- visit(param);
821
- if (param.typeAnnotation) {
822
- visit(param.typeAnnotation);
823
- }
824
- }
825
- }
826
-
827
- if (node.returnType) {
828
- visit(node.returnType);
829
- }
830
-
831
- if (node.body) {
832
- visit(node.body);
833
- }
834
- return;
835
- } else if (node.type === 'VariableDeclaration') {
836
- // Visit declarators in order
837
- if (node.declarations) {
838
- for (const declarator of node.declarations) {
839
- visit(declarator);
840
- }
841
- }
842
- return;
843
- } else if (node.type === 'VariableDeclarator') {
844
- // Visit in source order: id, typeAnnotation, init
845
- if (node.id) {
846
- visit(node.id);
847
- // Visit type annotation if present
848
- if (node.id.typeAnnotation) {
849
- visit(node.id.typeAnnotation);
850
- }
851
- }
852
- if (node.init) {
853
- visit(node.init);
854
- }
855
- return;
856
- } else if (node.type === 'IfStatement') {
857
- // Visit in source order: test, consequent, alternate
858
- if (node.test) {
859
- visit(node.test);
860
- }
861
-
862
- if (node.consequent) {
863
- if (node.consequent.loc) {
864
- // We're mapping only the brackets because mapping the whole thing
865
- // would be way too broad and causes
866
- // issues with partial mapping of something inside the body that we need
867
- tokens.push(
868
- {
869
- source: '{',
870
- generated: '{',
871
- loc: {
872
- start: {
873
- line: node.consequent.loc.start.line,
874
- column: node.consequent.loc.start.column,
875
- },
876
- end: {
877
- line: node.consequent.loc.start.line,
878
- column: node.consequent.loc.start.column + 1,
879
- },
880
- },
881
- metadata: {},
882
- mappingData: mapping_data_verify_only,
883
- },
884
- {
885
- source: '}',
886
- generated: '}',
887
- loc: {
888
- start: {
889
- line: node.consequent.loc.end.line,
890
- column: node.consequent.loc.end.column - 1,
891
- },
892
- end: {
893
- line: node.consequent.loc.end.line,
894
- column: node.consequent.loc.end.column,
895
- },
896
- },
897
- metadata: {},
898
- mappingData: mapping_data_verify_only,
899
- },
900
- );
901
- }
902
-
903
- visit(node.consequent);
904
- }
905
-
906
- if (node.alternate) {
907
- if (node.alternate.loc) {
908
- tokens.push(
909
- {
910
- source: '{',
911
- generated: '{',
912
- loc: {
913
- start: {
914
- line: node.alternate.loc.start.line,
915
- column: node.alternate.loc.start.column,
916
- },
917
- end: {
918
- line: node.alternate.loc.start.line,
919
- column: node.alternate.loc.start.column + 1,
920
- },
921
- },
922
- metadata: {},
923
- mappingData: mapping_data_verify_only,
924
- },
925
- {
926
- source: '}',
927
- generated: '}',
928
- loc: {
929
- start: {
930
- line: node.alternate.loc.end.line,
931
- column: node.alternate.loc.end.column - 1,
932
- },
933
- end: { line: node.alternate.loc.end.line, column: node.alternate.loc.end.column },
934
- },
935
- metadata: {},
936
- mappingData: mapping_data_verify_only,
937
- },
938
- );
939
- }
940
-
941
- visit(node.alternate);
942
- }
943
-
944
- return;
945
- } else if (node.type === 'ForStatement') {
946
- // Visit in source order: init, test, update, body
947
- if (node.init) {
948
- visit(node.init);
949
- }
950
- if (node.test) {
951
- visit(node.test);
952
- }
953
- if (node.update) {
954
- visit(node.update);
955
- }
956
- if (node.body) {
957
- visit(node.body);
958
- }
959
-
960
- mappings.push(
961
- get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
962
- );
963
- return;
964
- } else if (node.type === 'ForOfStatement' || node.type === 'ForInStatement') {
965
- // Visit in source order: left, right, index (Ripple-specific), body
966
- if (node.left) {
967
- visit(node.left);
968
- }
969
- if (node.right) {
970
- visit(node.right);
971
- }
972
- // Ripple-specific: index variable
973
- if (/** @type {AST.ForOfStatement} */ (node).index) {
974
- visit(/** @type {AST.ForOfStatement} */ (node).index);
975
- }
976
- if (node.body) {
977
- visit(node.body);
978
- }
979
-
980
- mappings.push(
981
- get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
982
- );
983
-
984
- return;
985
- } else if (node.type === 'WhileStatement' || node.type === 'DoWhileStatement') {
986
- // Visit in source order: test, body (while) or body, test (do-while)
987
- if (node.type === 'WhileStatement') {
988
- if (node.test) {
989
- visit(node.test);
990
- }
991
- if (node.body) {
992
- visit(node.body);
993
- }
994
- } else {
995
- if (node.body) {
996
- visit(node.body);
997
- }
998
- if (node.test) {
999
- visit(node.test);
1000
- }
1001
- }
1002
- return;
1003
- } else if (node.type === 'TryStatement') {
1004
- // Visit in source order: block, pending, handler, finalizer
1005
- if (node.block) {
1006
- mappings.push(
1007
- get_mapping_from_node(
1008
- node.block,
1009
- src_to_gen_map,
1010
- gen_line_offsets,
1011
- mapping_data_verify_only,
1012
- ),
1013
- );
1014
- visit(node.block);
1015
- }
1016
- if (node.pending) {
1017
- mappings.push(
1018
- get_mapping_from_node(
1019
- node.pending,
1020
- src_to_gen_map,
1021
- gen_line_offsets,
1022
- mapping_data_verify_only,
1023
- ),
1024
- );
1025
-
1026
- // Add a special token for the 'pending' keyword with customData
1027
- // to suppress TypeScript diagnostics and provide custom hover/definition
1028
- const pending = /** @type {(typeof node.pending) & AST.NodeWithLocation} */ (
1029
- node.pending
1030
- );
1031
- const pendingKeywordLoc = {
1032
- start: {
1033
- line: pending.loc.start.line,
1034
- column: pending.loc.start.column - 'pending '.length,
1035
- },
1036
- end: {
1037
- line: pending.loc.start.line,
1038
- column: pending.loc.start.column - 1,
1039
- },
1040
- };
1041
- tokens.push({
1042
- source: 'pending',
1043
- generated: 'pending',
1044
- loc: pendingKeywordLoc,
1045
- metadata: {
1046
- wordHighlight: {
1047
- /** @type {DocumentHighlightKind} */
1048
- kind: 1,
1049
- },
1050
- suppressedDiagnostics: [
1051
- 1472, // 'catch' or 'finally' expected
1052
- 2304, // Cannot find name 'pending'
1053
- ],
1054
- // suppress all hovers
1055
- hover: false,
1056
-
1057
- // Example of a custom hover contents (uses markdown)
1058
- // hover: '```ripple\npending\n```\n\nRipple-specific keyword for try/pending blocks.\n\nThe `pending` block executes while async operations inside the `try` block are awaiting. This provides a built-in loading state for async components.',
1059
-
1060
- // Example of a custom definition and its type definition file
1061
- // definition: {
1062
- // typeReplace: {
1063
- // name: 'SomeType',
1064
- // path: 'types/index.d.ts',
1065
- // },
1066
- // },
1067
- },
1068
- });
1069
- visit(node.pending);
1070
- }
1071
- if (node.handler) {
1072
- visit(node.handler);
1073
- }
1074
- if (node.finalizer) {
1075
- visit(node.finalizer);
1076
- }
1077
- return;
1078
- } else if (node.type === 'CatchClause') {
1079
- // Visit in source order: param, resetParam, body
1080
- if (node.param) {
1081
- visit(node.param);
1082
- }
1083
- if (node.resetParam) {
1084
- visit(node.resetParam);
1085
- }
1086
- if (node.body) {
1087
- visit(node.body);
1088
- }
1089
- return;
1090
- } else if (node.type === 'CallExpression' || node.type === 'NewExpression') {
1091
- if (node.type === 'NewExpression' && node.loc) {
1092
- mappings.push(
1093
- get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
1094
- );
1095
- }
1096
-
1097
- if (node.arguments) {
1098
- for (const arg of node.arguments) {
1099
- visit(arg);
1100
- }
1101
- }
1102
-
1103
- if (node.typeArguments) {
1104
- visit(node.typeArguments);
1105
- }
1106
-
1107
- if (node.callee) {
1108
- visit(node.callee);
1109
- }
1110
- return;
1111
- } else if (node.type === 'LogicalExpression' || node.type === 'BinaryExpression') {
1112
- // Visit in source order: left, right
1113
- if (node.left) {
1114
- visit(node.left);
1115
- }
1116
- if (node.right) {
1117
- visit(node.right);
1118
- }
1119
- return;
1120
- } else if (node.type === 'MemberExpression') {
1121
- if (node.loc) {
1122
- const mapping = get_mapping_from_node(
1123
- node,
1124
- src_to_gen_map,
1125
- gen_line_offsets,
1126
- mapping_data_verify_only,
1127
- );
1128
-
1129
- mappings.push(mapping);
1130
- }
1131
-
1132
- if (node.object) {
1133
- visit(node.object);
1134
- }
1135
- if (node.property) {
1136
- visit(node.property);
1137
-
1138
- if (node.computed && node.property.loc) {
1139
- mappings.push(
1140
- get_mapping_from_node(
1141
- node.property,
1142
- src_to_gen_map,
1143
- gen_line_offsets,
1144
- mapping_data_verify_only,
1145
- ),
1146
- );
1147
- }
1148
- }
1149
- return;
1150
- } else if (node.type === 'AssignmentExpression' || node.type === 'AssignmentPattern') {
1151
- // Visit in source order: left, typeAnnotation, right
1152
- if (node.left) {
1153
- visit(node.left);
1154
- // Visit type annotation if present (for AssignmentPattern)
1155
- if (node.left.typeAnnotation) {
1156
- visit(node.left.typeAnnotation);
1157
- }
1158
- }
1159
- if (node.right) {
1160
- visit(node.right);
1161
- }
1162
-
1163
- if (node.type === 'AssignmentPattern') {
1164
- // We need a mapping for the whole AssignmentPattern for diagnostics
1165
- // Only enable diagnostic verification here to avoid duplicate mappings
1166
- // that can cause things like double definitions
1167
- mappings.push(
1168
- get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
1169
- );
1170
- }
1171
-
1172
- return;
1173
- } else if (node.type === 'ObjectExpression' || node.type === 'ObjectPattern') {
1174
- if (node.loc && node.type === 'ObjectExpression') {
1175
- mappings.push(
1176
- get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
1177
- );
1178
- }
1179
-
1180
- // Visit properties in order
1181
- if (node.properties) {
1182
- for (const prop of node.properties) {
1183
- visit(prop);
1184
- }
1185
- }
1186
- return;
1187
- } else if (node.type === 'Property') {
1188
- // Visit in source order: key, value
1189
- // For shorthand properties ({ count }), key and value are the same node, only visit once
1190
- if (node.shorthand) {
1191
- if (node.value) {
1192
- visit(node.value);
1193
- }
1194
- } else {
1195
- if (node.computed) {
1196
- set_bracket_computed_mapping(node, mappings);
1197
- }
1198
-
1199
- if (
1200
- node.value.type === 'FunctionExpression' &&
1201
- node.method &&
1202
- node.value.metadata.is_component
1203
- ) {
1204
- set_component_mapping_to_name(/** @type {PropertyIsComponent} */ (node));
1205
- }
1206
-
1207
- if (node.key.type === 'Literal') {
1208
- handle_literal(
1209
- node.key,
1210
- /** @type {AST.FunctionExpression} */ (node.value).metadata.is_component,
1211
- );
1212
- } else {
1213
- visit(node.key);
1214
- }
1215
-
1216
- if (node.value) {
1217
- visit(node.value);
1218
- }
1219
- }
1220
- return;
1221
- } else if (node.type === 'ArrayExpression' || node.type === 'ArrayPattern') {
1222
- // Visit elements in order
1223
- if (node.elements) {
1224
- for (const element of node.elements) {
1225
- if (element) visit(element);
1226
- }
1227
- }
1228
- return;
1229
- } else if (node.type === 'ConditionalExpression') {
1230
- // Visit in source order: test, consequent, alternate
1231
- if (node.test) {
1232
- visit(node.test);
1233
- }
1234
- if (node.consequent) {
1235
- visit(node.consequent);
1236
- }
1237
- if (node.alternate) {
1238
- visit(node.alternate);
1239
- }
1240
- return;
1241
- } else if (node.type === 'UnaryExpression' || node.type === 'UpdateExpression') {
1242
- // Visit argument
1243
- if (node.argument) {
1244
- visit(node.argument);
1245
- }
1246
- return;
1247
- } else if (node.type === 'TemplateLiteral') {
1248
- if (node.loc) {
1249
- mappings.push(
1250
- get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
1251
- );
1252
- }
1253
-
1254
- // Visit quasis and expressions in order
1255
- for (let i = 0; i < node.quasis.length; i++) {
1256
- if (node.quasis[i]) {
1257
- visit(node.quasis[i]);
1258
- }
1259
- if (i < node.expressions.length && node.expressions[i]) {
1260
- visit(node.expressions[i]);
1261
- }
1262
- }
1263
- return;
1264
- } else if (node.type === 'TaggedTemplateExpression') {
1265
- // Visit in source order: tag, quasi
1266
- if (node.tag) {
1267
- visit(node.tag);
1268
- }
1269
- if (node.quasi) {
1270
- visit(node.quasi);
1271
- }
1272
- return;
1273
- } else if (node.type === 'ReturnStatement' || node.type === 'ThrowStatement') {
1274
- // Visit argument
1275
- if (node.argument) {
1276
- visit(node.argument);
1277
- }
1278
-
1279
- if (node.type === 'ReturnStatement' && node.loc) {
1280
- const mapping = get_mapping_from_node(
1281
- node,
1282
- src_to_gen_map,
1283
- gen_line_offsets,
1284
- mapping_data_verify_only,
1285
- );
1286
- // We're only mapping the 'return' keyword, otherwise the mapping would be too broad
1287
- // and likely may cause issues with partial mappings of something inside the return statement that we need
1288
- const return_keyword_length = 'return'.length;
1289
- mapping.lengths = [return_keyword_length];
1290
- mapping.generatedLengths = [return_keyword_length];
1291
-
1292
- mappings.push(mapping);
1293
- }
1294
- return;
1295
- } else if (node.type === 'ExpressionStatement') {
1296
- if (node.expression) {
1297
- visit(node.expression);
1298
- }
1299
- return;
1300
- } else if (node.type === 'BlockStatement' || node.type === 'Program') {
1301
- // Visit body statements in order
1302
- if (node.body) {
1303
- for (const statement of node.body) {
1304
- visit(statement);
1305
- }
1306
- }
1307
- return;
1308
- } else if (node.type === 'SwitchStatement') {
1309
- // Visit in source order: discriminant, cases
1310
- if (node.discriminant) {
1311
- visit(node.discriminant);
1312
- }
1313
- if (node.cases) {
1314
- for (const caseNode of node.cases) {
1315
- visit(caseNode);
1316
- }
1317
- }
1318
-
1319
- mappings.push(
1320
- get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
1321
- );
1322
-
1323
- return;
1324
- } else if (node.type === 'SwitchCase') {
1325
- // Visit in source order: test, consequent
1326
- if (node.test) {
1327
- visit(node.test);
1328
- }
1329
- if (node.consequent) {
1330
- for (const statement of node.consequent) {
1331
- visit(statement);
1332
- }
1333
- }
1334
- return;
1335
- } else if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {
1336
- // Visit in source order: id, superClass, body
1337
- if (node.id) {
1338
- visit(node.id);
1339
- }
1340
- if (node.superClass) {
1341
- visit(node.superClass);
1342
- }
1343
- if (node.body) {
1344
- visit(node.body);
1345
- }
1346
- return;
1347
- } else if (node.type === 'ClassBody') {
1348
- // Visit body in order
1349
- if (node.body) {
1350
- for (const member of node.body) {
1351
- visit(member);
1352
- }
1353
- }
1354
- return;
1355
- } else if (node.type === 'MethodDefinition') {
1356
- if (node.computed) {
1357
- set_bracket_computed_mapping(node, mappings);
1358
- }
1359
-
1360
- if (node.value.metadata.is_component) {
1361
- set_component_mapping_to_name(/** @type {MethodIsComponent} */ (node));
1362
- }
1363
-
1364
- if (node.key.type === 'Literal') {
1365
- handle_literal(
1366
- node.key,
1367
- /** @type {AST.FunctionExpression} */ (node.value).metadata.is_component,
1368
- );
1369
- } else {
1370
- visit(node.key);
1371
- }
1372
-
1373
- if (node.value) {
1374
- visit(node.value);
1375
- }
1376
- return;
1377
- } else if (node.type === 'SequenceExpression') {
1378
- // Visit expressions in order
1379
- if (node.expressions) {
1380
- for (const expr of node.expressions) {
1381
- visit(expr);
1382
- }
1383
- }
1384
- return;
1385
- } else if (node.type === 'SpreadElement' || node.type === 'RestElement') {
1386
- // Visit the argument
1387
- if (node.argument) {
1388
- visit(node.argument);
1389
- // Visit type annotation if present (for RestElement)
1390
- if (/** @type {AST.Pattern} */ (node.argument).typeAnnotation) {
1391
- visit(/** @type {AST.Pattern} */ (node.argument).typeAnnotation);
1392
- }
1393
- }
1394
- // RestElement itself can have typeAnnotation
1395
- if (node.typeAnnotation) {
1396
- visit(node.typeAnnotation);
1397
- }
1398
- return;
1399
- } else if (node.type === 'YieldExpression' || node.type === 'AwaitExpression') {
1400
- // Visit the argument if present
1401
- if (node.argument) {
1402
- visit(node.argument);
1403
- }
1404
-
1405
- if (node.type === 'AwaitExpression') {
1406
- const max_len = 'await'.length;
1407
- // We need a mapping for diagnostics but only on the 'await' keyword
1408
- const mapping = get_mapping_from_node(
1409
- node,
1410
- src_to_gen_map,
1411
- gen_line_offsets,
1412
- mapping_data_verify_only,
1413
- max_len,
1414
- max_len,
1415
- );
1416
-
1417
- mappings.push(mapping);
1418
- }
1419
- return;
1420
- } else if (node.type === 'ChainExpression') {
1421
- // Visit the expression
1422
- if (node.expression) {
1423
- visit(node.expression);
1424
- }
1425
- return;
1426
- } else if (node.type === 'Super' || node.type === 'ThisExpression') {
1427
- // Leaf nodes, no children
1428
- return;
1429
- } else if (node.type === 'MetaProperty') {
1430
- // Visit meta and property (e.g., new.target, import.meta)
1431
- if (node.meta) {
1432
- visit(node.meta);
1433
- }
1434
- if (node.property) {
1435
- visit(node.property);
1436
- }
1437
- return;
1438
- } else if (node.type === 'EmptyStatement' || node.type === 'DebuggerStatement') {
1439
- // No children to visit
1440
- return;
1441
- } else if (node.type === 'LabeledStatement') {
1442
- // Visit label and statement
1443
- if (node.label) {
1444
- visit(node.label);
1445
- }
1446
- if (node.body) {
1447
- visit(node.body);
1448
- }
1449
- return;
1450
- } else if (node.type === 'BreakStatement' || node.type === 'ContinueStatement') {
1451
- // Visit label if present
1452
- if (node.label) {
1453
- visit(node.label);
1454
- }
1455
- return;
1456
- } else if (node.type === 'WithStatement') {
1457
- // Visit object and body
1458
- if (node.object) {
1459
- visit(node.object);
1460
- }
1461
- if (node.body) {
1462
- visit(node.body);
1463
- }
1464
- return;
1465
- } else if (node.type === 'JSXFragment') {
1466
- // Visit children in order
1467
- if (node.children) {
1468
- for (const child of node.children) {
1469
- visit(/** @type {AST.Node} */ (child));
1470
- }
1471
- }
1472
- return;
1473
- } else if (node.type === 'JSXClosingFragment' || node.type === 'JSXOpeningFragment') {
1474
- // These are handled by their parent nodes
1475
- return;
1476
- } else if (node.type === 'JSXMemberExpression') {
1477
- // Visit object and property (e.g., <Foo.Bar>)
1478
- if (node.object) {
1479
- visit(node.object);
1480
- }
1481
- if (node.property) {
1482
- visit(node.property);
1483
- }
1484
- return;
1485
- } else if (node.type === 'JSXNamespacedName') {
1486
- // Visit namespace and name (e.g., <svg:circle>)
1487
- if (node.namespace) {
1488
- visit(node.namespace);
1489
- }
1490
- if (node.name) {
1491
- visit(node.name);
1492
- }
1493
- return;
1494
- } else if (node.type === 'JSXEmptyExpression') {
1495
- // No children
1496
- return;
1497
- } else if (node.type === 'TemplateElement') {
1498
- // Leaf node, no children to visit
1499
- return;
1500
- } else if (node.type === 'PrivateIdentifier') {
1501
- // Leaf node
1502
- return;
1503
- } else if (node.type === 'PropertyDefinition') {
1504
- // Visit key and value
1505
- if (node.key) {
1506
- visit(node.key);
1507
- }
1508
- if (node.value) {
1509
- visit(node.value);
1510
- }
1511
- return;
1512
- } else if (node.type === 'StaticBlock') {
1513
- // Visit body
1514
- if (node.body) {
1515
- for (const statement of node.body) {
1516
- visit(statement);
1517
- }
1518
- }
1519
- return;
1520
- } else if (node.type === 'ImportExpression') {
1521
- // Visit source
1522
- if (node.source) {
1523
- visit(node.source);
1524
- }
1525
- return;
1526
- } else if (node.type === 'ParenthesizedExpression') {
1527
- if (node.metadata.forceMapping && node.loc) {
1528
- const mapping = get_mapping_from_node(node, src_to_gen_map, gen_line_offsets);
1529
- if (node.metadata.skipParenthesisMapping) {
1530
- mapping.generatedOffsets[0] = mapping.generatedOffsets[0] + 1; // Skip the opening parenthesis
1531
- mapping.generatedLengths[0] = mapping.generatedLengths[0] - 2; // Skip both parentheses
1532
- }
1533
- mappings.push(mapping);
1534
- }
1535
- // Visit the wrapped expression
1536
- if (node.expression) {
1537
- visit(node.expression);
1538
- }
1539
- return;
1540
- } else if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression') {
1541
- // Type assertion: value as Type
1542
- if (node.expression) {
1543
- visit(node.expression);
1544
- }
1545
- if (node.typeAnnotation) {
1546
- visit(node.typeAnnotation);
1547
- }
1548
- return;
1549
- } else if (node.type === 'TSNonNullExpression') {
1550
- // Non-null assertion: value!
1551
- if (node.expression) {
1552
- visit(node.expression);
1553
- }
1554
- return;
1555
- } else if (node.type === 'TSTypeAssertion') {
1556
- // Type assertion: <Type>value
1557
- if (node.expression) {
1558
- visit(node.expression);
1559
- }
1560
- // Skip typeAnnotation
1561
- return;
1562
- } else if (
1563
- node.type === 'TSTypeParameterInstantiation' ||
1564
- node.type === 'TSTypeParameterDeclaration'
1565
- ) {
1566
- if (node.loc) {
1567
- mappings.push(
1568
- get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
1569
- );
1570
- }
1571
- // Generic type parameters - visit to collect type variable names
1572
- if (node.params) {
1573
- for (const param of node.params) {
1574
- visit(param);
1575
- }
1576
- }
1577
- return;
1578
- } else if (node.type === 'TSTypeParameter') {
1579
- // Type parameter like T in <T> or key in mapped types
1580
- // Note: node.name is a string, not an Identifier node
1581
- if (node.name && node.loc && typeof node.name === 'string') {
1582
- tokens.push({ source: node.name, generated: node.name, loc: node.loc, metadata: {} });
1583
- } else if (node.name && typeof node.name === 'object') {
1584
- // In some cases, name might be an Identifier node
1585
- visit(node.name);
1586
- }
1587
- if (node.constraint) {
1588
- visit(node.constraint);
1589
- }
1590
- if (node.default) {
1591
- visit(node.default);
1592
- }
1593
- return;
1594
- } else if (node.type === 'TSTypeAnnotation') {
1595
- // Type annotation - visit the type
1596
- if (node.typeAnnotation) {
1597
- visit(node.typeAnnotation);
1598
- }
1599
- return;
1600
- } else if (node.type === 'TSTypeReference') {
1601
- // Type reference like "string" or "Array<T>"
1602
- if (node.typeName) {
1603
- visit(node.typeName);
1604
- }
1605
-
1606
- // typeParameters and typeArguments (different parsers use different names)
1607
- // tsTypeParameters is a bug in the estree-typescript
1608
- // but we fixed in the analyzer to typeArguments.
1609
-
1610
- if (node.typeArguments) {
1611
- visit(node.typeArguments);
1612
- }
1613
- return;
1614
- } else if (node.type === 'TSQualifiedName') {
1615
- // Qualified name (e.g., Foo.Bar in types)
1616
- if (node.left) {
1617
- visit(node.left);
1618
- }
1619
- if (node.right) {
1620
- visit(node.right);
1621
- }
1622
- return;
1623
- } else if (node.type === 'TSArrayType') {
1624
- // Array type like T[]
1625
- if (node.elementType) {
1626
- visit(node.elementType);
1627
- }
1628
- return;
1629
- } else if (node.type === 'TSTupleType') {
1630
- // Tuple type like [string, number]
1631
- if (node.elementTypes) {
1632
- for (const type of node.elementTypes) {
1633
- visit(type);
1634
- }
1635
- }
1636
- return;
1637
- } else if (node.type === 'TSUnionType' || node.type === 'TSIntersectionType') {
1638
- // Union (A | B) or Intersection (A & B) types
1639
- if (node.types) {
1640
- for (const type of node.types) {
1641
- visit(type);
1642
- }
1643
- }
1644
- return;
1645
- } else if (node.type === 'TSFunctionType' || node.type === 'TSConstructorType') {
1646
- // Function or constructor type
1647
- if (node.typeParameters) {
1648
- visit(node.typeParameters);
1649
- }
1650
- if (node.parameters) {
1651
- for (const param of node.parameters) {
1652
- visit(param);
1653
- // Visit type annotation on the parameter
1654
- if (
1655
- /** @type {Exclude<AST.Parameter, AST.TSParameterProperty>} */ (param).typeAnnotation
1656
- ) {
1657
- visit(
1658
- /** @type {Exclude<AST.Parameter, AST.TSParameterProperty>} */ (param)
1659
- .typeAnnotation,
1660
- );
1661
- }
1662
- }
1663
- }
1664
- if (node.typeAnnotation) {
1665
- visit(node.typeAnnotation);
1666
- }
1667
- return;
1668
- } else if (node.type === 'TSTypeLiteral') {
1669
- // Object type literal { foo: string }
1670
- if (node.members) {
1671
- for (const member of node.members) {
1672
- visit(member);
1673
- }
1674
- }
1675
- return;
1676
- } else if (node.type === 'TSPropertySignature') {
1677
- // Property signature in type
1678
- if (node.key) {
1679
- visit(node.key);
1680
- }
1681
- if (node.typeAnnotation) {
1682
- visit(node.typeAnnotation);
1683
- }
1684
- return;
1685
- } else if (node.type === 'TSMethodSignature') {
1686
- // Method signature in type
1687
- if (node.key) {
1688
- visit(node.key);
1689
- }
1690
- if (node.typeParameters) {
1691
- visit(node.typeParameters);
1692
- }
1693
- if (node.parameters) {
1694
- for (const param of node.parameters) {
1695
- visit(param);
1696
- // Visit type annotation on the parameter
1697
- if (
1698
- /** @type {Exclude<AST.Parameter, AST.TSParameterProperty>} */ (param).typeAnnotation
1699
- ) {
1700
- visit(
1701
- /** @type {Exclude<AST.Parameter, AST.TSParameterProperty>} */ (param)
1702
- .typeAnnotation,
1703
- );
1704
- }
1705
- }
1706
- }
1707
- if (node.typeAnnotation) {
1708
- visit(node.typeAnnotation);
1709
- }
1710
- return;
1711
- } else if (node.type === 'TSIndexSignature') {
1712
- // Index signature [key: string]: Type
1713
- if (node.parameters) {
1714
- for (const param of node.parameters) {
1715
- visit(param);
1716
- // Visit type annotation on the parameter
1717
- if (
1718
- /** @type {Exclude<AST.Parameter, AST.TSParameterProperty>} */ (param).typeAnnotation
1719
- ) {
1720
- visit(
1721
- /** @type {Exclude<AST.Parameter, AST.TSParameterProperty>} */ (param)
1722
- .typeAnnotation,
1723
- );
1724
- }
1725
- }
1726
- }
1727
- if (node.typeAnnotation) {
1728
- visit(node.typeAnnotation);
1729
- }
1730
- return;
1731
- } else if (
1732
- node.type === 'TSCallSignatureDeclaration' ||
1733
- node.type === 'TSConstructSignatureDeclaration'
1734
- ) {
1735
- // Call or construct signature
1736
- if (node.typeParameters) {
1737
- visit(node.typeParameters);
1738
- }
1739
- if (node.parameters) {
1740
- for (const param of node.parameters) {
1741
- visit(param);
1742
- // Visit type annotation on the parameter
1743
- if (
1744
- /** @type {Exclude<AST.Parameter, AST.TSParameterProperty>} */ (param).typeAnnotation
1745
- ) {
1746
- visit(
1747
- /** @type {Exclude<AST.Parameter, AST.TSParameterProperty>} */ (param)
1748
- .typeAnnotation,
1749
- );
1750
- }
1751
- }
1752
- }
1753
- if (node.typeAnnotation) {
1754
- visit(node.typeAnnotation);
1755
- }
1756
- return;
1757
- } else if (node.type === 'TSConditionalType') {
1758
- // Conditional type: T extends U ? X : Y
1759
- if (node.checkType) {
1760
- visit(node.checkType);
1761
- }
1762
- if (node.extendsType) {
1763
- visit(node.extendsType);
1764
- }
1765
- if (node.trueType) {
1766
- visit(node.trueType);
1767
- }
1768
- if (node.falseType) {
1769
- visit(node.falseType);
1770
- }
1771
- return;
1772
- } else if (node.type === 'TSInferType') {
1773
- // Infer type: infer T
1774
- if (node.typeParameter) {
1775
- visit(node.typeParameter);
1776
- }
1777
- return;
1778
- } else if (node.type === 'TSParenthesizedType') {
1779
- // Parenthesized type: (T)
1780
- if (node.typeAnnotation) {
1781
- visit(node.typeAnnotation);
1782
- }
1783
- return;
1784
- } else if (node.type === 'TSTypeOperator') {
1785
- // Type operator: keyof T, readonly T
1786
- if (node.typeAnnotation) {
1787
- visit(node.typeAnnotation);
1788
- }
1789
- return;
1790
- } else if (node.type === 'TSIndexedAccessType') {
1791
- // Indexed access: T[K]
1792
- if (node.objectType) {
1793
- visit(node.objectType);
1794
- }
1795
- if (node.indexType) {
1796
- visit(node.indexType);
1797
- }
1798
- return;
1799
- } else if (node.type === 'TSMappedType') {
1800
- // Mapped type: { [K in keyof T]: ... }
1801
- if (node.typeParameter) {
1802
- visit(node.typeParameter);
1803
- }
1804
- if (node.typeAnnotation) {
1805
- visit(node.typeAnnotation);
1806
- }
1807
- return;
1808
- } else if (node.type === 'TSLiteralType') {
1809
- // Literal type: "foo" | 123 | true
1810
- if (node.literal) {
1811
- visit(node.literal);
1812
- }
1813
- return;
1814
- } else if (node.type === 'TSExpressionWithTypeArguments') {
1815
- // Expression with type arguments: Foo<Bar>
1816
- if (node.expression) {
1817
- visit(node.expression);
1818
- }
1819
- if (node.typeParameters) {
1820
- visit(node.typeParameters);
1821
- }
1822
- return;
1823
- } else if (node.type === 'TSImportType') {
1824
- // Import type: import("module").Type
1825
- if (node.argument) {
1826
- visit(node.argument);
1827
- }
1828
- if (node.qualifier) {
1829
- visit(node.qualifier);
1830
- }
1831
- if (node.typeParameters) {
1832
- visit(node.typeParameters);
1833
- }
1834
- return;
1835
- } else if (node.type === 'TSTypeQuery') {
1836
- // Type query: typeof x
1837
- if (node.exprName) {
1838
- visit(node.exprName);
1839
- }
1840
- if (node.typeArguments) {
1841
- visit(node.typeArguments);
1842
- }
1843
- return;
1844
- } else if (node.type === 'TSInterfaceDeclaration') {
1845
- // Interface declaration
1846
- if (node.id) {
1847
- visit(node.id);
1848
- }
1849
- if (node.typeParameters) {
1850
- visit(node.typeParameters);
1851
- }
1852
- if (node.extends) {
1853
- for (const ext of node.extends) {
1854
- visit(ext);
1855
- }
1856
- }
1857
- if (node.body) {
1858
- visit(node.body);
1859
- }
1860
- return;
1861
- } else if (node.type === 'TSInterfaceBody') {
1862
- // Interface body
1863
- if (node.body) {
1864
- for (const member of node.body) {
1865
- visit(member);
1866
- }
1867
- }
1868
- return;
1869
- } else if (node.type === 'TSTypeAliasDeclaration') {
1870
- // Type alias
1871
- if (node.id) {
1872
- visit(node.id);
1873
- }
1874
- if (node.typeParameters) {
1875
- visit(node.typeParameters);
1876
- }
1877
- if (node.typeAnnotation) {
1878
- visit(node.typeAnnotation);
1879
- }
1880
- return;
1881
- } else if (node.type === 'TSEnumDeclaration') {
1882
- // Visit id and members
1883
- if (node.id) {
1884
- visit(node.id);
1885
- }
1886
- if (node.members) {
1887
- for (const member of node.members) {
1888
- visit(member);
1889
- }
1890
- }
1891
- return;
1892
- } else if (node.type === 'TSEnumMember') {
1893
- // Visit id and initializer
1894
- if (node.id) {
1895
- visit(node.id);
1896
- }
1897
- if (node.initializer) {
1898
- visit(node.initializer);
1899
- }
1900
- return;
1901
- } else if (node.type === 'TSModuleDeclaration') {
1902
- // Namespace/module declaration
1903
- if (node.id) {
1904
- visit(node.id);
1905
- }
1906
- if (node.body) {
1907
- visit(node.body);
1908
- }
1909
- return;
1910
- } else if (node.type === 'TSModuleBlock') {
1911
- // Module body
1912
- if (node.body) {
1913
- for (const statement of node.body) {
1914
- visit(statement);
1915
- }
1916
- }
1917
- return;
1918
- } else if (node.type === 'TSNamedTupleMember') {
1919
- // Named tuple member: [name: Type]
1920
- if (node.label) {
1921
- visit(node.label);
1922
- }
1923
- if (node.elementType) {
1924
- visit(node.elementType);
1925
- }
1926
- return;
1927
- } else if (node.type === 'TSRestType') {
1928
- // Rest type: ...T[]
1929
- if (node.typeAnnotation) {
1930
- visit(node.typeAnnotation);
1931
- }
1932
- return;
1933
- } else if (node.type === 'TSOptionalType') {
1934
- // Optional type: T?
1935
- if (node.typeAnnotation) {
1936
- visit(node.typeAnnotation);
1937
- }
1938
- return;
1939
- } else if (
1940
- node.type === 'TSAnyKeyword' ||
1941
- node.type === 'TSUnknownKeyword' ||
1942
- node.type === 'TSNumberKeyword' ||
1943
- node.type === 'TSObjectKeyword' ||
1944
- node.type === 'TSBooleanKeyword' ||
1945
- node.type === 'TSBigIntKeyword' ||
1946
- node.type === 'TSStringKeyword' ||
1947
- node.type === 'TSSymbolKeyword' ||
1948
- node.type === 'TSVoidKeyword' ||
1949
- node.type === 'TSUndefinedKeyword' ||
1950
- node.type === 'TSNullKeyword' ||
1951
- node.type === 'TSNeverKeyword' ||
1952
- node.type === 'TSThisType' ||
1953
- node.type === 'TSIntrinsicKeyword'
1954
- ) {
1955
- // Primitive type keywords - leaf nodes, no children
1956
- return;
1957
- } else if (node.type === 'TSDeclareFunction') {
1958
- // TypeScript declare function: declare function foo(): void;
1959
- // Visit in source order: id, typeParameters, params, returnType
1960
- if (node.id) {
1961
- visit(node.id);
1962
- }
1963
- if (node.typeParameters) {
1964
- visit(node.typeParameters);
1965
- }
1966
- if (node.params) {
1967
- for (const param of node.params) {
1968
- visit(param);
1969
- }
1970
- }
1971
- if (node.returnType) {
1972
- visit(node.returnType);
1973
- }
1974
- return;
1975
- } else if (node.type === 'TSExportAssignment') {
1976
- // TypeScript export assignment: export = foo;
1977
- if (node.expression) {
1978
- visit(node.expression);
1979
- }
1980
- return;
1981
- } else if (node.type === 'TSNamespaceExportDeclaration') {
1982
- // TypeScript namespace export: export as namespace foo;
1983
- if (node.id) {
1984
- visit(node.id);
1985
- }
1986
- return;
1987
- } else if (node.type === 'TSExternalModuleReference') {
1988
- // TypeScript external module reference: import foo = require('bar');
1989
- if (node.expression) {
1990
- visit(node.expression);
1991
- }
1992
- return;
1993
- } else if (node.type === 'TSInstantiationExpression') {
1994
- // TypeScript instantiation expression: new Foo<T>()
1995
- if (node.expression) {
1996
- visit(node.expression);
1997
- }
1998
- if (node.typeArguments) {
1999
- visit(node.typeArguments);
2000
- }
2001
- return;
2002
- }
2003
-
2004
- throw new Error(`Unhandled AST node type in mapping walker: ${node.type}`);
2005
- },
2006
- });
2007
-
2008
- for (const token of tokens) {
2009
- const source_text = token.source ?? '';
2010
- const gen_text = token.generated;
2011
- const source_start = loc_to_offset(
2012
- token.loc.start.line,
2013
- token.loc.start.column,
2014
- src_line_offsets,
2015
- );
2016
- const source_length = source_text.length;
2017
- const gen_length = gen_text.length;
2018
- const gen_line_col = get_generated_position(
2019
- token.loc.start.line,
2020
- token.loc.start.column,
2021
- src_to_gen_map,
2022
- );
2023
- const gen_start = loc_to_offset(gen_line_col.line, gen_line_col.column, gen_line_offsets);
2024
-
2025
- /** @type {CustomMappingData} */
2026
- const customData = {};
2027
-
2028
- // Add optional metadata from token if present
2029
- if ('wordHighlight' in token.metadata) {
2030
- customData.wordHighlight = token.metadata.wordHighlight;
2031
- }
2032
- if ('suppressedDiagnostics' in token.metadata) {
2033
- customData.suppressedDiagnostics = token.metadata.suppressedDiagnostics;
2034
- }
2035
- if ('hover' in token.metadata) {
2036
- customData.hover = token.metadata.hover;
2037
- }
2038
- if ('definition' in token.metadata) {
2039
- customData.definition = token.metadata.definition;
2040
- }
2041
-
2042
- mappings.push({
2043
- sourceOffsets: [source_start],
2044
- generatedOffsets: [gen_start],
2045
- lengths: [source_length],
2046
- generatedLengths: [gen_length],
2047
- data: {
2048
- ...(token.mappingData ?? mapping_data),
2049
- customData,
2050
- },
2051
- });
2052
- }
2053
-
2054
- // Sort mappings by start position, but prioritize narrower ranges that are fully contained
2055
- // within wider ones. This ensures that specific tokens (like identifiers) take precedence
2056
- // over broader ranges (like `if` consequent blocks) during language server lookups.
2057
- // Otherwise, volar may pick the wrong mapping for diagnostics or other features.
2058
- mappings.sort((a, b) => {
2059
- const aStart = a.sourceOffsets[0];
2060
- const aEnd = aStart + a.lengths[0];
2061
- const bStart = b.sourceOffsets[0];
2062
- const bEnd = bStart + b.lengths[0];
2063
-
2064
- if (aStart === bStart && aEnd === bEnd) {
2065
- // ranges are identical
2066
- return 0;
2067
- }
2068
-
2069
- // Check if one range is fully contained within the other
2070
- const bInsideA = bStart >= aStart && bEnd <= aEnd;
2071
- const aInsideB = aStart >= bStart && aEnd <= bEnd;
2072
-
2073
- if (bInsideA) {
2074
- // B (narrower) should come first
2075
- return 1;
2076
- }
2077
- if (aInsideB) {
2078
- // A (narrower) should come first
2079
- return -1;
2080
- }
2081
-
2082
- // Neither contains the other - sort by start position
2083
- return aStart - bStart;
2084
- });
2085
-
2086
- // Add a mapping for the very beginning of the file to handle import additions
2087
- // This ensures that code actions adding imports at the top work correctly
2088
- if (!isImportDeclarationPresent && mappings.length > 0 && mappings[0].sourceOffsets[0] > 0) {
2089
- mappings.unshift({
2090
- sourceOffsets: [0],
2091
- generatedOffsets: [0],
2092
- lengths: [1],
2093
- generatedLengths: [1],
2094
- data: {
2095
- ...mapping_data,
2096
- customData: {},
2097
- },
2098
- });
2099
- }
2100
-
2101
- /** @type {CodeMapping[]} */
2102
- const cssMappings = [];
2103
- for (let i = 0; i < css_regions.length; i++) {
2104
- const region = css_regions[i];
2105
- cssMappings.push({
2106
- sourceOffsets: [region.start],
2107
- generatedOffsets: [0],
2108
- lengths: [region.content.length],
2109
- generatedLengths: [region.content.length],
2110
- data: {
2111
- ...mapping_data,
2112
- customData: {
2113
- embeddedId: region.id,
2114
- content: region.content,
2115
- },
2116
- },
2117
- });
2118
- }
2119
-
2120
- return {
2121
- code: generated_code,
2122
- mappings,
2123
- cssMappings,
2124
- };
2125
- }