@tsrx/core 0.1.63 → 0.1.65
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 +2 -2
- package/src/analyze/index.js +65 -18
- package/src/analyze/validation.js +19 -0
- package/src/diagnostics.js +1 -0
- package/src/index.js +3 -0
- package/src/plugin.js +100 -37
- package/src/source-map-utils.js +21 -66
- package/src/transform/jsx/index.js +48 -24
- package/src/transform/lazy.js +527 -72
- package/src/transform/segments.js +17 -4
- package/src/utils/ast.js +90 -7
- package/types/index.d.ts +2 -0
package/src/source-map-utils.js
CHANGED
|
@@ -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}
|
|
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 {[
|
|
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
|
-
|
|
110
|
+
_generated_code,
|
|
114
111
|
include_source_line_generated_map = false,
|
|
115
112
|
) {
|
|
116
|
-
/** @type {
|
|
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 (
|
|
176
|
-
const line_segments
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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,
|
|
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 {
|
|
249
|
-
* @returns {
|
|
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 {
|
|
268
|
-
* @returns {
|
|
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 {
|
|
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 {
|
|
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,
|
|
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;`
|
|
865
|
-
//
|
|
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
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
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
|
|
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))
|
|
4111
|
+
if (!local_bindings.has(name)) candidate_bindings.set(name, binding);
|
|
4112
|
+
}
|
|
4091
4113
|
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
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;
|