@tsrx/core 0.1.62 → 0.1.64

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.
@@ -1,20 +1,17 @@
1
1
  /**
2
2
  * @import { PostProcessingChanges, LineOffsets } from '../types/index.js';
3
3
  * @import * as AST from 'estree';
4
- * @import {
5
- * CodeMapping,
6
- * CodePosition,
7
- * CodeToGeneratedMap,
8
- * GeneratedToSourceMap,
9
- * SourceLineGeneratedMap,
10
- * SourceLineGeneratedPosition,
11
- * } from '../types/index.js';
4
+ * @import { CodeMapping, CodePosition } from '../types/index.js';
12
5
  * @import { CodeMapping as VolarCodeMapping } from '@volar/language-core';
13
6
  * @import { RawSourceMap } from 'source-map';
14
7
  */
15
8
 
16
9
  import { decode } from '@jridgewell/sourcemap-codec';
17
10
 
11
+ /** @typedef {Pick<CodePosition, 'line' | 'column'>} GeneratedPosition */
12
+ /** @typedef {Map<string, GeneratedPosition[]>} SourceToGeneratedMap */
13
+ /** @typedef {Map<number, Array<{ column: number, position: GeneratedPosition }>>} SourceLineGeneratedMap */
14
+
18
15
  /** @type {VolarCodeMapping['data']} */
19
16
  export const mapping_data = {
20
17
  verification: true,
@@ -102,21 +99,19 @@ export const offset_to_line_col = (offset, line_offsets) => {
102
99
  * @param {RawSourceMap} source_map - The source map object from esrap (v3 format)
103
100
  * @param {PostProcessingChanges} post_processing_changes - Optional post-processing changes to apply
104
101
  * @param {LineOffsets} line_offsets - Pre-computed line offsets array
105
- * @param {string} generated_code - The final generated code (after post-processing)
102
+ * @param {string} _generated_code - Retained positional slot for existing internal callers
106
103
  * @param {boolean} [include_source_line_generated_map] - Whether to build the optional source-line predecessor lookup
107
- * @returns {[CodeToGeneratedMap, GeneratedToSourceMap, SourceLineGeneratedMap | null]} Tuple of [source-to-generated map, generated-to-source map, source-line generated map]
104
+ * @returns {[SourceToGeneratedMap, null, SourceLineGeneratedMap | null]} Tuple of [source-to-generated map, unused reverse-map slot, source-line generated map]
108
105
  */
109
106
  export function build_src_to_gen_map(
110
107
  source_map,
111
108
  post_processing_changes,
112
109
  line_offsets,
113
- generated_code,
110
+ _generated_code,
114
111
  include_source_line_generated_map = false,
115
112
  ) {
116
- /** @type {CodeToGeneratedMap} */
113
+ /** @type {SourceToGeneratedMap} */
117
114
  const map = new Map();
118
- /** @type {GeneratedToSourceMap} */
119
- const reverse_map = new Map();
120
115
  /** @type {SourceLineGeneratedMap | null} */
121
116
  const source_line_generated_map = include_source_line_generated_map ? new Map() : null;
122
117
 
@@ -172,81 +167,41 @@ export function build_src_to_gen_map(
172
167
  }
173
168
 
174
169
  // Now build the map using adjusted positions
175
- for (let line_idx = 0; line_idx < adjusted_segments.length; line_idx++) {
176
- const line_segments = adjusted_segments[line_idx];
177
-
178
- for (let seg_idx = 0; seg_idx < line_segments.length; seg_idx++) {
179
- const segment = line_segments[seg_idx];
170
+ for (const line_segments of adjusted_segments) {
171
+ for (const segment of line_segments) {
180
172
  const line = segment.line;
181
173
  const column = segment.column;
182
174
 
183
- // Determine end position using next segment
184
- let end_line = line;
185
- let end_column = column;
186
-
187
- // Look for next segment to determine end position
188
- if (seg_idx + 1 < line_segments.length) {
189
- // Next segment on same line
190
- const next_segment = line_segments[seg_idx + 1];
191
- end_line = next_segment.line;
192
- end_column = next_segment.column;
193
- } else if (
194
- line_idx + 1 < adjusted_segments.length &&
195
- adjusted_segments[line_idx + 1].length > 0
196
- ) {
197
- // Look at first segment of next line
198
- const next_segment = adjusted_segments[line_idx + 1][0];
199
- end_line = next_segment.line;
200
- end_column = next_segment.column;
201
- }
202
-
203
- // Extract code snippet
204
- const start_offset = line_col_to_byte_offset(line, column);
205
- const end_offset = line_col_to_byte_offset(end_line, end_column);
206
- const code_snippet = generated_code.slice(start_offset, end_offset);
207
-
208
175
  // Create key from source position (1-indexed line, 0-indexed column)
209
176
  segment.sourceLine += 1;
210
177
  const key = `${segment.sourceLine}:${segment.sourceColumn}`;
211
178
 
212
- // Store adjusted generated position with code snippet
213
- const gen_pos = { line, column, end_line, end_column, code: code_snippet, metadata: {} };
179
+ const gen_pos = { line, column };
214
180
 
215
181
  if (!map.has(key)) {
216
182
  map.set(key, []);
217
183
  }
218
- /** @type {CodePosition[]} */ (map.get(key)).push(gen_pos);
184
+ /** @type {GeneratedPosition[]} */ (map.get(key)).push(gen_pos);
219
185
  if (source_line_generated_map) {
220
186
  if (!source_line_generated_map.has(segment.sourceLine)) {
221
187
  source_line_generated_map.set(segment.sourceLine, []);
222
188
  }
223
- /** @type {SourceLineGeneratedPosition[]} */ (
189
+ /** @type {Array<{ column: number, position: GeneratedPosition }>} */ (
224
190
  source_line_generated_map.get(segment.sourceLine)
225
191
  ).push({ column: segment.sourceColumn, position: gen_pos });
226
192
  }
227
-
228
- // Store reverse mapping (generated to source)
229
- const gen_key = `${gen_pos.line}:${gen_pos.column}`;
230
-
231
- if (!reverse_map.has(gen_key)) {
232
- reverse_map.set(gen_key, []);
233
- }
234
- reverse_map.get(gen_key)?.push({
235
- line: segment.sourceLine,
236
- column: segment.sourceColumn,
237
- });
238
193
  }
239
194
  }
240
195
 
241
- return [map, reverse_map, source_line_generated_map];
196
+ return [map, null, source_line_generated_map];
242
197
  }
243
198
 
244
199
  /**
245
200
  * Look up generated position for a given source position if it exists
246
201
  * @param {number} src_line - 1-based line number in source
247
202
  * @param {number} src_column - 0-based column number in source
248
- * @param {CodeToGeneratedMap} src_to_gen_map - Lookup map
249
- * @returns {CodePosition | Error} Generated position
203
+ * @param {SourceToGeneratedMap} src_to_gen_map - Lookup map
204
+ * @returns {GeneratedPosition | Error} Generated position
250
205
  */
251
206
  function maybe_get_generated_position(src_line, src_column, src_to_gen_map) {
252
207
  const key = `${src_line}:${src_column}`;
@@ -264,8 +219,8 @@ function maybe_get_generated_position(src_line, src_column, src_to_gen_map) {
264
219
  * Look up generated position for a given source position
265
220
  * @param {number} src_line - 1-based line number in source
266
221
  * @param {number} src_column - 0-based column number in source
267
- * @param {CodeToGeneratedMap} src_to_gen_map - Lookup map
268
- * @returns {CodePosition} Generated position
222
+ * @param {SourceToGeneratedMap} src_to_gen_map - Lookup map
223
+ * @returns {GeneratedPosition} Generated position
269
224
  */
270
225
  export function get_generated_position(src_line, src_column, src_to_gen_map) {
271
226
  const maybe_position = maybe_get_generated_position(src_line, src_column, src_to_gen_map);
@@ -332,7 +287,7 @@ export function find_exact_mapping(mappings, source_offset, generated_offset, le
332
287
  * DO NOT EXPORT THIS FUNCTION!
333
288
  * THE FIX NEEDS TO HAPPEN IN THE TRANSFORMER, SEGMENTS OR PARSER
334
289
  * @param {AST.Node | AST.NodeWithLocation} node
335
- * @param {CodeToGeneratedMap} src_to_gen_map
290
+ * @param {SourceToGeneratedMap} src_to_gen_map
336
291
  * @param {number[]} gen_line_offsets
337
292
  * @param {Partial<VolarCodeMapping['data']>} [filtered_data]
338
293
  * @param {number} [src_max_len]
@@ -379,7 +334,7 @@ function __maybe_get_mapping_from_node(
379
334
 
380
335
  /**
381
336
  * @param {AST.Node | AST.NodeWithLocation} node
382
- * @param {CodeToGeneratedMap} src_to_gen_map
337
+ * @param {SourceToGeneratedMap} src_to_gen_map
383
338
  * @param {number[]} gen_line_offsets
384
339
  * @param {Partial<VolarCodeMapping['data']>} [filtered_data]
385
340
  * @param {number} [src_max_len]
@@ -858,11 +858,13 @@ export function createJsxTransform(platform) {
858
858
 
859
859
  // Apply lazy destructuring transforms to module-level code (top-level function
860
860
  // declarations, arrow functions, etc.).
861
- // In type-only mode, the lazy patterns survive untouched: esrap ignores the
861
+ // In type-only mode, ordinary lazy patterns survive untouched: esrap ignores the
862
862
  // non-standard `lazy` flag, so `&{ a, b }` prints as `{ a, b }`, `let &[a]
863
863
  // = expr` prints as `let [a] = expr`, and the bare statement-level form
864
- // `&[x] = expr;` (used when `x` is already declared) prints as `[x] =
865
- // expr;` a valid destructuring assignment to the existing binding.
864
+ // `&[x] = expr;` standalone assignment is the exception: it must take the
865
+ // same declaration path as runtime output so transparent editor-only wrappers
866
+ // cannot turn it into an eager destructure. The assignment-only preallocation
867
+ // below leaves params and declarations on their existing type-only path.
866
868
  //
867
869
  // Re-run `preallocate_lazy_ids` first. The initial pre-walk pass stamps
868
870
  // `metadata.has_lazy_descendants` (the fast-path gate that tells
@@ -874,12 +876,10 @@ export function createJsxTransform(platform) {
874
876
  // stamps them too (it is idempotent: already-allocated `lazy_id`s are kept),
875
877
  // so lazy bindings declared inside a nested block or directive body are
876
878
  // rewritten just like a flat function body.
877
- if (!transform_context.typeOnly) {
878
- preallocate_lazy_ids(lowered_program, transform_context);
879
- }
880
- const final_program = transform_context.typeOnly
881
- ? lowered_program
882
- : /** @type {AST.Program} */ (apply_lazy_transforms(lowered_program, new Map()));
879
+ preallocate_lazy_ids(lowered_program, transform_context, transform_context.typeOnly);
880
+ const final_program = /** @type {AST.Program} */ (
881
+ apply_lazy_transforms(lowered_program, new Map())
882
+ );
883
883
 
884
884
  const result = print(
885
885
  final_program,
@@ -3084,27 +3084,35 @@ function collect_pattern_bindings(pattern, bindings) {
3084
3084
  /**
3085
3085
  * Check if a node references any of the given scope bindings.
3086
3086
  * Used to determine if a JSX element is static and can be hoisted to module level.
3087
+ * When a result set is provided, records every referenced binding instead of
3088
+ * stopping after the first match.
3087
3089
  *
3088
3090
  * @param {AST.Node | null | undefined} node
3089
3091
  * @param {Map<string, AST.Identifier>} scope_bindings
3092
+ * @param {Set<string>} [referenced_bindings]
3090
3093
  * @returns {boolean}
3091
3094
  */
3092
- function references_scope_bindings(node, scope_bindings) {
3095
+ function references_scope_bindings(node, scope_bindings, referenced_bindings) {
3093
3096
  if (!node) return false;
3094
3097
  if (scope_bindings.size === 0) return false;
3095
3098
 
3096
3099
  if (node.type === 'Identifier') {
3097
- return scope_bindings.has(node.name);
3100
+ const references_binding = scope_bindings.has(node.name);
3101
+ if (references_binding) referenced_bindings?.add(node.name);
3102
+ return references_binding;
3098
3103
  }
3099
3104
 
3100
3105
  // JSXIdentifier is a variable reference when capitalized (tag name like <MyComponent />)
3101
3106
  // or when it's the object of a JSXMemberExpression (e.g. ui in <ui.Button />)
3102
3107
  if (node.type === 'JSXIdentifier') {
3103
- return scope_bindings.has(node.name);
3108
+ const references_binding = scope_bindings.has(node.name);
3109
+ if (references_binding) referenced_bindings?.add(node.name);
3110
+ return references_binding;
3104
3111
  }
3105
3112
 
3106
3113
  // Not `child_nodes`: several keys are labels rather than references and must
3107
3114
  // be skipped based on the owning node's type.
3115
+ let references_binding = false;
3108
3116
  const entries = /** @type {AST.TraversableAstNode} */ (node);
3109
3117
  for (const key of Object.keys(entries)) {
3110
3118
  if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
@@ -3123,16 +3131,25 @@ function references_scope_bindings(node, scope_bindings) {
3123
3131
 
3124
3132
  const value = entries[key];
3125
3133
  if (Array.isArray(value)) {
3126
- if (
3127
- value.some((item) => is_ast_node(item) && references_scope_bindings(item, scope_bindings))
3128
- )
3129
- return true;
3130
- } else if (is_ast_node(value) && references_scope_bindings(value, scope_bindings)) {
3131
- return true;
3134
+ for (const item of value) {
3135
+ if (
3136
+ is_ast_node(item) &&
3137
+ references_scope_bindings(item, scope_bindings, referenced_bindings)
3138
+ ) {
3139
+ if (!referenced_bindings) return true;
3140
+ references_binding = true;
3141
+ }
3142
+ }
3143
+ } else if (
3144
+ is_ast_node(value) &&
3145
+ references_scope_bindings(value, scope_bindings, referenced_bindings)
3146
+ ) {
3147
+ if (!referenced_bindings) return true;
3148
+ references_binding = true;
3132
3149
  }
3133
3150
  }
3134
3151
 
3135
- return false;
3152
+ return references_binding;
3136
3153
  }
3137
3154
 
3138
3155
  /**
@@ -4081,18 +4098,25 @@ function get_referenced_helper_bindings(body_nodes, available_bindings) {
4081
4098
  const helper_bindings = [];
4082
4099
  /** @type {Map<string, AST.Identifier>} */
4083
4100
  const local_bindings = new Map();
4101
+ /** @type {Map<string, AST.Identifier>} */
4102
+ const candidate_bindings = new Map();
4103
+ /** @type {Set<string>} */
4104
+ const referenced_bindings = new Set();
4084
4105
 
4085
4106
  for (const node of body_nodes) {
4086
4107
  collect_statement_bindings(node, local_bindings);
4087
4108
  }
4088
4109
 
4089
4110
  for (const [name, binding] of available_bindings) {
4090
- if (local_bindings.has(name)) continue;
4111
+ if (!local_bindings.has(name)) candidate_bindings.set(name, binding);
4112
+ }
4091
4113
 
4092
- const scope = new Map([[name, binding]]);
4093
- if (body_nodes.some((node) => references_scope_bindings(node, scope))) {
4094
- helper_bindings.push(binding);
4095
- }
4114
+ for (const node of body_nodes) {
4115
+ references_scope_bindings(node, candidate_bindings, referenced_bindings);
4116
+ }
4117
+
4118
+ for (const [name, binding] of available_bindings) {
4119
+ if (referenced_bindings.has(name)) helper_bindings.push(binding);
4096
4120
  }
4097
4121
 
4098
4122
  return helper_bindings;