@tsrx/core 0.1.20 → 0.1.24
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 +3 -3
- package/src/analyze/prune.js +117 -70
- package/src/analyze/validation.js +122 -5
- package/src/diagnostics.js +1 -0
- package/src/index.js +10 -4
- package/src/parse/index.js +157 -99
- package/src/plugin.js +2540 -867
- package/src/runtime/html.js +1 -1
- package/src/runtime/iterable.js +15 -13
- package/src/runtime/language-helpers.js +39 -0
- package/src/scope.js +25 -10
- package/src/source-map-utils.js +1 -1
- package/src/transform/await.js +5 -1
- package/src/transform/jsx/ast-builders.js +6 -81
- package/src/transform/jsx/helpers.js +8 -5
- package/src/transform/jsx/index.js +1440 -451
- package/src/transform/jsx-interleave.js +1 -2
- package/src/transform/scoping.js +26 -63
- package/src/transform/segments.js +66 -48
- package/src/transform/style-ref.js +3 -11
- package/src/utils/builders.js +2 -3
- package/types/index.d.ts +181 -115
- package/types/jsx-platform.d.ts +14 -11
- package/types/parse.d.ts +36 -10
- package/types/runtime/html.d.ts +1 -0
- package/types/runtime/language-helpers.d.ts +4 -0
- package/types/runtime/ref.d.ts +1 -0
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
* change what earlier children rendered.
|
|
16
16
|
*
|
|
17
17
|
* The `is_jsx_child` predicate is target-specific — each target recognizes
|
|
18
|
-
*
|
|
19
|
-
* `TSRXExpression`, etc. plus plain JSX nodes).
|
|
18
|
+
* its JSX-bearing child nodes and template-control expressions.
|
|
20
19
|
*
|
|
21
20
|
* @param {any[]} body_nodes
|
|
22
21
|
* @param {(node: any) => boolean} is_jsx_child
|
package/src/transform/scoping.js
CHANGED
|
@@ -37,45 +37,14 @@ export function prepare_stylesheet_for_render(stylesheet) {
|
|
|
37
37
|
* @returns {boolean}
|
|
38
38
|
*/
|
|
39
39
|
export function is_style_element(node) {
|
|
40
|
-
return
|
|
41
|
-
node &&
|
|
42
|
-
node.type === 'Element' &&
|
|
43
|
-
node.id &&
|
|
44
|
-
node.id.type === 'Identifier' &&
|
|
45
|
-
node.id.name === 'style'
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @param {any} node
|
|
51
|
-
* @returns {boolean}
|
|
52
|
-
*/
|
|
53
|
-
export function is_composite_element(node) {
|
|
54
|
-
if (!node || node.type !== 'Element' || !node.id) {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (node.id.type === 'Identifier') {
|
|
59
|
-
return /^[A-Z]/.test(node.id.name);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return node.id.type === 'MemberExpression';
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @param {any} node
|
|
67
|
-
* @returns {boolean}
|
|
68
|
-
*/
|
|
69
|
-
function is_style_jsx_element(node) {
|
|
70
|
-
const name = node?.openingElement?.name;
|
|
71
|
-
return node?.type === 'JSXElement' && name?.type === 'JSXIdentifier' && name.name === 'style';
|
|
40
|
+
return !!node && node.type === 'JSXStyleElement';
|
|
72
41
|
}
|
|
73
42
|
|
|
74
43
|
/**
|
|
75
44
|
* @param {any} node
|
|
76
45
|
* @returns {boolean}
|
|
77
46
|
*/
|
|
78
|
-
function is_composite_jsx_element(node) {
|
|
47
|
+
export function is_composite_jsx_element(node) {
|
|
79
48
|
const name = node?.openingElement?.name;
|
|
80
49
|
if (node?.type !== 'JSXElement' || !name) {
|
|
81
50
|
return false;
|
|
@@ -89,7 +58,7 @@ function is_composite_jsx_element(node) {
|
|
|
89
58
|
}
|
|
90
59
|
|
|
91
60
|
/**
|
|
92
|
-
* Recursively walk
|
|
61
|
+
* Recursively walk native JSX nodes within a TSRX fragment and add the hash
|
|
93
62
|
* class name so scope-qualified selectors (e.g. `.foo.hash`) match.
|
|
94
63
|
*
|
|
95
64
|
* @param {any} node
|
|
@@ -113,34 +82,26 @@ export function annotate_with_hash(
|
|
|
113
82
|
return node;
|
|
114
83
|
}
|
|
115
84
|
|
|
116
|
-
if (node.type === '
|
|
117
|
-
if (
|
|
118
|
-
node
|
|
119
|
-
return node;
|
|
120
|
-
}
|
|
121
|
-
if (!is_style_element(node) && !is_composite_element(node)) {
|
|
122
|
-
add_hash_class(node, hash, jsx_class_attr_name);
|
|
85
|
+
if (node.type === 'JSXElement') {
|
|
86
|
+
if (!is_composite_jsx_element(node) || node.metadata?.runtime_dynamic_element) {
|
|
87
|
+
add_hash_class_to_jsx_element(node, hash, jsx_class_attr_name);
|
|
123
88
|
}
|
|
124
89
|
if (Array.isArray(node.children)) {
|
|
125
90
|
node.children = node.children
|
|
126
|
-
.filter((/** @type {any} */ child) => preserve_style_elements || !is_style_element(child))
|
|
127
91
|
.map((/** @type {any} */ child) =>
|
|
128
92
|
annotate_with_hash(child, hash, jsx_class_attr_name, preserve_style_elements),
|
|
129
|
-
)
|
|
93
|
+
)
|
|
94
|
+
.filter(Boolean);
|
|
130
95
|
}
|
|
131
96
|
return node;
|
|
132
97
|
}
|
|
133
98
|
|
|
134
|
-
if (node.type === '
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (Array.isArray(node.children)) {
|
|
139
|
-
node.children = node.children.map((/** @type {any} */ child) =>
|
|
140
|
-
annotate_with_hash(child, hash, jsx_class_attr_name, preserve_style_elements),
|
|
141
|
-
);
|
|
99
|
+
if (node.type === 'JSXStyleElement') {
|
|
100
|
+
if (preserve_style_elements) {
|
|
101
|
+
node.children = [];
|
|
102
|
+
return node;
|
|
142
103
|
}
|
|
143
|
-
return
|
|
104
|
+
return null;
|
|
144
105
|
}
|
|
145
106
|
|
|
146
107
|
for (const key of Object.keys(node)) {
|
|
@@ -192,25 +153,22 @@ export function annotate_component_with_hash(
|
|
|
192
153
|
* @returns {void}
|
|
193
154
|
*/
|
|
194
155
|
export function add_hash_class(element, hash, class_attr_name = 'class') {
|
|
195
|
-
const attrs = element.
|
|
156
|
+
const attrs = element.openingElement.attributes;
|
|
196
157
|
const existing = attrs.find(
|
|
197
158
|
(/** @type {any} */ a) =>
|
|
198
|
-
a.type === '
|
|
159
|
+
a.type === 'JSXAttribute' &&
|
|
199
160
|
a.name &&
|
|
200
|
-
a.name.type === '
|
|
161
|
+
a.name.type === 'JSXIdentifier' &&
|
|
201
162
|
(a.name.name === 'class' || a.name.name === 'className'),
|
|
202
163
|
);
|
|
203
164
|
|
|
204
165
|
if (!existing) {
|
|
205
|
-
attrs.push(
|
|
206
|
-
type: 'Attribute',
|
|
207
|
-
name: b.id(class_attr_name),
|
|
208
|
-
value: { type: 'Literal', value: hash, raw: JSON.stringify(hash) },
|
|
209
|
-
});
|
|
166
|
+
attrs.push(b.jsx_attribute(b.jsx_id(class_attr_name), b.literal(hash)));
|
|
210
167
|
return;
|
|
211
168
|
}
|
|
212
169
|
|
|
213
|
-
const value =
|
|
170
|
+
const value =
|
|
171
|
+
existing.value?.type === 'JSXExpressionContainer' ? existing.value.expression : existing.value;
|
|
214
172
|
if (!value) {
|
|
215
173
|
existing.value = { type: 'Literal', value: hash, raw: JSON.stringify(hash) };
|
|
216
174
|
return;
|
|
@@ -224,8 +182,9 @@ export function add_hash_class(element, hash, class_attr_name = 'class') {
|
|
|
224
182
|
}
|
|
225
183
|
|
|
226
184
|
// Dynamic expression. Concatenate at runtime via template literal.
|
|
227
|
-
|
|
228
|
-
|
|
185
|
+
existing.value = b.jsx_expression_container(
|
|
186
|
+
b.template([b.quasi('', false), b.quasi(` ${hash}`, true)], [value]),
|
|
187
|
+
);
|
|
229
188
|
}
|
|
230
189
|
|
|
231
190
|
/**
|
|
@@ -247,12 +206,14 @@ function add_hash_class_to_jsx_element(element, hash, jsx_class_attr_name) {
|
|
|
247
206
|
const hash_literal = b.literal(hash);
|
|
248
207
|
/** @type {any} */ (hash_literal).raw = JSON.stringify(hash);
|
|
249
208
|
attrs.push(b.jsx_attribute(b.jsx_id(jsx_class_attr_name), hash_literal));
|
|
209
|
+
element.attributes = attrs;
|
|
250
210
|
return;
|
|
251
211
|
}
|
|
252
212
|
|
|
253
213
|
const value = existing.value;
|
|
254
214
|
if (!value) {
|
|
255
215
|
existing.value = { type: 'Literal', value: hash, raw: JSON.stringify(hash) };
|
|
216
|
+
element.attributes = attrs;
|
|
256
217
|
return;
|
|
257
218
|
}
|
|
258
219
|
|
|
@@ -260,6 +221,7 @@ function add_hash_class_to_jsx_element(element, hash, jsx_class_attr_name) {
|
|
|
260
221
|
const merged = `${value.value} ${hash}`;
|
|
261
222
|
value.value = merged;
|
|
262
223
|
value.raw = JSON.stringify(merged);
|
|
224
|
+
element.attributes = attrs;
|
|
263
225
|
return;
|
|
264
226
|
}
|
|
265
227
|
|
|
@@ -267,4 +229,5 @@ function add_hash_class_to_jsx_element(element, hash, jsx_class_attr_name) {
|
|
|
267
229
|
existing.value = b.jsx_expression_container(
|
|
268
230
|
b.template([b.quasi('', false), b.quasi(` ${hash}`, true)], [expression]),
|
|
269
231
|
);
|
|
232
|
+
element.attributes = attrs;
|
|
270
233
|
}
|
|
@@ -55,6 +55,7 @@ import {
|
|
|
55
55
|
build_line_offsets,
|
|
56
56
|
get_mapping_from_node,
|
|
57
57
|
} from '../source-map-utils.js';
|
|
58
|
+
import { should_preserve_comment } from '../comment-utils.js';
|
|
58
59
|
|
|
59
60
|
const LAZY_PARAM_IDENTIFIER_REGEX = /^__lazy\d+$/;
|
|
60
61
|
|
|
@@ -133,9 +134,8 @@ function get_style_region_id(hash, fallback) {
|
|
|
133
134
|
function visit_source_ast(ast, src_line_offsets, { regions, css_element_info }) {
|
|
134
135
|
let region_id = 0;
|
|
135
136
|
walk(ast, null, {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (node.id?.type === 'Identifier' && node.id?.name === 'style' && node.css) {
|
|
137
|
+
JSXStyleElement(node, context) {
|
|
138
|
+
if (node.css) {
|
|
139
139
|
const openLoc = /** @type {ESTreeJSX.JSXOpeningElement & AST.NodeWithLocation} */ (
|
|
140
140
|
node.openingElement
|
|
141
141
|
).loc;
|
|
@@ -156,8 +156,10 @@ function visit_source_ast(ast, src_line_offsets, { regions, css_element_info })
|
|
|
156
156
|
|
|
157
157
|
context.next();
|
|
158
158
|
},
|
|
159
|
-
|
|
160
|
-
const element = context.path?.findLast(
|
|
159
|
+
JSXAttribute(node, context) {
|
|
160
|
+
const element = context.path?.findLast(
|
|
161
|
+
(n) => n.type === 'JSXElement' && n.metadata?.native_tsrx,
|
|
162
|
+
);
|
|
161
163
|
if (element?.metadata?.css?.scopedClasses) {
|
|
162
164
|
// we don't need to check is_element_dom_element(node)
|
|
163
165
|
// since scopedClasses are added during pruning only to DOM elements
|
|
@@ -430,6 +432,42 @@ export function convert_source_map_to_mappings(
|
|
|
430
432
|
}
|
|
431
433
|
}
|
|
432
434
|
|
|
435
|
+
/** @type {Set<string>} */
|
|
436
|
+
const mapped_comments = new Set();
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* @param {any} node
|
|
440
|
+
* @returns {void}
|
|
441
|
+
*/
|
|
442
|
+
function add_preserved_comment_mappings(node) {
|
|
443
|
+
for (const key of ['leadingComments', 'trailingComments', 'innerComments', 'comments']) {
|
|
444
|
+
const comments = node?.[key];
|
|
445
|
+
if (!Array.isArray(comments)) continue;
|
|
446
|
+
|
|
447
|
+
for (const comment of comments) {
|
|
448
|
+
if (!comment?.loc || !should_preserve_comment(comment)) continue;
|
|
449
|
+
|
|
450
|
+
const comment_key = `${comment.start}:${comment.end}`;
|
|
451
|
+
if (mapped_comments.has(comment_key)) continue;
|
|
452
|
+
mapped_comments.add(comment_key);
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
mappings.push(
|
|
456
|
+
get_mapping_from_node(
|
|
457
|
+
comment,
|
|
458
|
+
src_to_gen_map,
|
|
459
|
+
gen_line_offsets,
|
|
460
|
+
mapping_data_verify_only,
|
|
461
|
+
),
|
|
462
|
+
);
|
|
463
|
+
} catch {
|
|
464
|
+
// Comments that were not emitted in generated TSX have no source-map
|
|
465
|
+
// segment. They should not produce Volar mappings.
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
433
471
|
/**
|
|
434
472
|
* @param {AST.Literal} node
|
|
435
473
|
*/
|
|
@@ -444,6 +482,8 @@ export function convert_source_map_to_mappings(
|
|
|
444
482
|
|
|
445
483
|
walk(ast, null, {
|
|
446
484
|
_(node, { visit }) {
|
|
485
|
+
add_preserved_comment_mappings(node);
|
|
486
|
+
|
|
447
487
|
// Collect key node types: Identifiers, Literals, and JSX Elements
|
|
448
488
|
if (node.type === 'Identifier') {
|
|
449
489
|
// Only create mappings for identifiers with location info (from source)
|
|
@@ -738,6 +778,14 @@ export function convert_source_map_to_mappings(
|
|
|
738
778
|
} else if (node.type === 'JSXText') {
|
|
739
779
|
// Text content, no tokens to collect
|
|
740
780
|
return;
|
|
781
|
+
} else if (node.type === 'JSXCodeBlock') {
|
|
782
|
+
for (const statement of node.body) {
|
|
783
|
+
visit(statement);
|
|
784
|
+
}
|
|
785
|
+
if (node.render) {
|
|
786
|
+
visit(node.render);
|
|
787
|
+
}
|
|
788
|
+
return;
|
|
741
789
|
} else if (node.type === 'JSXElement') {
|
|
742
790
|
// Manually visit in source order: opening element, children, closing element
|
|
743
791
|
|
|
@@ -1080,6 +1128,19 @@ export function convert_source_map_to_mappings(
|
|
|
1080
1128
|
visit(node.body);
|
|
1081
1129
|
}
|
|
1082
1130
|
|
|
1131
|
+
if (node.type === 'ForOfStatement' && node.empty) {
|
|
1132
|
+
mappings.push(
|
|
1133
|
+
get_mapping_from_node(
|
|
1134
|
+
node.empty,
|
|
1135
|
+
src_to_gen_map,
|
|
1136
|
+
gen_line_offsets,
|
|
1137
|
+
mapping_data_verify_only,
|
|
1138
|
+
),
|
|
1139
|
+
);
|
|
1140
|
+
|
|
1141
|
+
visit(node.empty);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1083
1144
|
if (node.loc) {
|
|
1084
1145
|
mappings.push(
|
|
1085
1146
|
get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
|
|
@@ -1128,49 +1189,6 @@ export function convert_source_map_to_mappings(
|
|
|
1128
1189
|
),
|
|
1129
1190
|
);
|
|
1130
1191
|
|
|
1131
|
-
// Add a special token for the 'pending' keyword with customData
|
|
1132
|
-
// to suppress TypeScript diagnostics and provide custom hover/definition
|
|
1133
|
-
const pending = /** @type {(typeof node.pending) & AST.NodeWithLocation} */ (
|
|
1134
|
-
node.pending
|
|
1135
|
-
);
|
|
1136
|
-
const pendingKeywordLoc = {
|
|
1137
|
-
start: {
|
|
1138
|
-
line: pending.loc.start.line,
|
|
1139
|
-
column: pending.loc.start.column - 'pending '.length,
|
|
1140
|
-
},
|
|
1141
|
-
end: {
|
|
1142
|
-
line: pending.loc.start.line,
|
|
1143
|
-
column: pending.loc.start.column - 1,
|
|
1144
|
-
},
|
|
1145
|
-
};
|
|
1146
|
-
tokens.push({
|
|
1147
|
-
source: 'pending',
|
|
1148
|
-
generated: 'pending',
|
|
1149
|
-
loc: pendingKeywordLoc,
|
|
1150
|
-
metadata: {
|
|
1151
|
-
wordHighlight: {
|
|
1152
|
-
/** @type {DocumentHighlightKind} */
|
|
1153
|
-
kind: 1,
|
|
1154
|
-
},
|
|
1155
|
-
suppressedDiagnostics: [
|
|
1156
|
-
1472, // 'catch' or 'finally' expected
|
|
1157
|
-
2304, // Cannot find name 'pending'
|
|
1158
|
-
],
|
|
1159
|
-
// suppress all hovers
|
|
1160
|
-
hover: false,
|
|
1161
|
-
|
|
1162
|
-
// Example of a custom hover contents (uses markdown)
|
|
1163
|
-
// 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.',
|
|
1164
|
-
|
|
1165
|
-
// Example of a custom definition and its type definition file
|
|
1166
|
-
// definition: {
|
|
1167
|
-
// typeReplace: {
|
|
1168
|
-
// name: 'SomeType',
|
|
1169
|
-
// path: 'types/index.d.ts',
|
|
1170
|
-
// },
|
|
1171
|
-
// },
|
|
1172
|
-
},
|
|
1173
|
-
});
|
|
1174
1192
|
visit(node.pending);
|
|
1175
1193
|
}
|
|
1176
1194
|
if (node.handler) {
|
|
@@ -68,7 +68,7 @@ export function collect_style_ref_attributes(node, refs = []) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
if (is_style_element(node)) {
|
|
71
|
-
for (const attr of node.attributes || []) {
|
|
71
|
+
for (const attr of node.openingElement?.attributes || []) {
|
|
72
72
|
if (is_ref_attribute(attr) && attr.value) {
|
|
73
73
|
refs.push(attr);
|
|
74
74
|
}
|
|
@@ -224,10 +224,7 @@ function get_ref_attribute_expression(attr) {
|
|
|
224
224
|
*/
|
|
225
225
|
function is_ref_attribute(attr) {
|
|
226
226
|
return (
|
|
227
|
-
|
|
228
|
-
(attr?.type === 'JSXAttribute' &&
|
|
229
|
-
attr.name?.type === 'JSXIdentifier' &&
|
|
230
|
-
attr.name.name === 'ref')
|
|
227
|
+
attr?.type === 'JSXAttribute' && attr.name?.type === 'JSXIdentifier' && attr.name.name === 'ref'
|
|
231
228
|
);
|
|
232
229
|
}
|
|
233
230
|
|
|
@@ -236,12 +233,7 @@ function is_ref_attribute(attr) {
|
|
|
236
233
|
* @returns {boolean}
|
|
237
234
|
*/
|
|
238
235
|
function is_style_element(node) {
|
|
239
|
-
return !!
|
|
240
|
-
node &&
|
|
241
|
-
node.type === 'Element' &&
|
|
242
|
-
node.id?.type === 'Identifier' &&
|
|
243
|
-
node.id.name === 'style'
|
|
244
|
-
);
|
|
236
|
+
return !!node && node.type === 'JSXStyleElement';
|
|
245
237
|
}
|
|
246
238
|
|
|
247
239
|
/**
|
package/src/utils/builders.js
CHANGED
|
@@ -283,7 +283,6 @@ export function export_builder(
|
|
|
283
283
|
* @param {AST.BlockStatement} body
|
|
284
284
|
* @param {boolean} [async]
|
|
285
285
|
* @param {AST.TSTypeParameterDeclaration} [type_parameters]
|
|
286
|
-
* @param
|
|
287
286
|
* @returns {AST.FunctionDeclaration}
|
|
288
287
|
*/
|
|
289
288
|
export function function_declaration(id, params, body, async = false, type_parameters) {
|
|
@@ -1125,7 +1124,7 @@ export function jsx_attribute(name, value = null, shorthand = false, loc_info) {
|
|
|
1125
1124
|
|
|
1126
1125
|
/**
|
|
1127
1126
|
* Build a fresh `JSXOpeningElement`. For elements derived from an existing
|
|
1128
|
-
*
|
|
1127
|
+
* JSX element node, prefer `jsx_element` which spreads from the source.
|
|
1129
1128
|
*
|
|
1130
1129
|
* @param {ESTreeJSX.JSXOpeningElement['name']} name
|
|
1131
1130
|
* @param {ESTreeJSX.JSXOpeningElement['attributes']} [attributes]
|
|
@@ -1200,7 +1199,7 @@ export function jsx_element_fresh(
|
|
|
1200
1199
|
}
|
|
1201
1200
|
|
|
1202
1201
|
/**
|
|
1203
|
-
* @param {
|
|
1202
|
+
* @param {ESTreeJSX.JSXElement} node
|
|
1204
1203
|
* @param {ESTreeJSX.JSXOpeningElement['attributes']} attributes
|
|
1205
1204
|
* @param {ESTreeJSX.JSXElement['children']} children
|
|
1206
1205
|
* @returns {ESTreeJSX.JSXElement}
|