@tsrx/core 0.1.19 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Core compiler infrastructure for TSRX syntax",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.1.19",
6
+ "version": "0.1.22",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
@@ -60,10 +60,10 @@
60
60
  "dependencies": {
61
61
  "@jridgewell/sourcemap-codec": "^1.5.5",
62
62
  "@noble/hashes": "^2.2.0",
63
- "@sveltejs/acorn-typescript": "^1.0.9",
63
+ "@sveltejs/acorn-typescript": "^1.0.10",
64
64
  "@types/estree-jsx": "^1.0.5",
65
65
  "@types/estree": "^1.0.8",
66
- "acorn": "^8.15.0",
66
+ "acorn": "^8.16.0",
67
67
  "esrap": "^2.2.8",
68
68
  "is-reference": "^3.0.3",
69
69
  "magic-string": "^0.30.18",
@@ -19,28 +19,75 @@ let style_identifier_classes;
19
19
  /** @type {TopScopedClasses} */
20
20
  let top_scoped_classes;
21
21
 
22
+ /**
23
+ * @param {any} node
24
+ * @returns {boolean}
25
+ */
26
+ function is_native_jsx_element(node) {
27
+ return node?.type === 'JSXElement' && node.metadata?.native_tsrx;
28
+ }
29
+
30
+ /**
31
+ * @param {any} node
32
+ * @returns {any}
33
+ */
34
+ function get_element_name(node) {
35
+ return node.openingElement?.name ?? node.id;
36
+ }
37
+
38
+ /**
39
+ * @param {any} node
40
+ * @returns {any[]}
41
+ */
42
+ function get_element_attributes(node) {
43
+ return node.openingElement?.attributes ?? node.attributes ?? [];
44
+ }
45
+
46
+ /**
47
+ * @param {any} attribute
48
+ * @returns {string | null}
49
+ */
50
+ function get_attribute_name(attribute) {
51
+ const name = attribute.name;
52
+ if (name?.type === 'JSXIdentifier' || name?.type === 'Identifier') return name.name;
53
+ return null;
54
+ }
55
+
56
+ /**
57
+ * @param {any} attribute
58
+ * @returns {any}
59
+ */
60
+ function get_attribute_value(attribute) {
61
+ const value = attribute.value;
62
+ return value?.type === 'JSXExpressionContainer' ? value.expression : value;
63
+ }
64
+
22
65
  /**
23
66
  * Returns true if node is a DOM element (not a component).
24
67
  * @param {AST.Node} node
25
68
  * @returns {boolean}
26
69
  */
27
70
  function is_element_dom_element(node) {
28
- const id = /** @type {AST.Element} */ (node).id;
71
+ const id = get_element_name(node);
29
72
  return (
30
- id.type === 'Identifier' &&
73
+ (id.type === 'Identifier' || id.type === 'JSXIdentifier') &&
31
74
  id.name[0].toLowerCase() === id.name[0] &&
32
75
  id.name !== 'children' &&
33
- !id.tracked
76
+ !id.tracked &&
77
+ !id.dynamic
34
78
  );
35
79
  }
36
80
 
37
81
  /**
38
82
  * Returns true if element is dynamic.
39
- * @param {AST.Element} node
83
+ * @param {any} node
40
84
  * @returns {boolean}
41
85
  */
42
86
  function is_element_dynamic(node) {
43
- return node.id.type === 'Identifier' ? !!node.id.tracked : false;
87
+ const id = get_element_name(node);
88
+ return id?.type === 'Identifier' || id?.type === 'JSXIdentifier'
89
+ ? !!(id.tracked || id.dynamic)
90
+ : false;
44
91
  }
45
92
 
46
93
  // CSS selector constants
@@ -220,7 +267,7 @@ function truncate(node) {
220
267
  /**
221
268
  * @param {AST.CSS.RelativeSelector[]} relative_selectors
222
269
  * @param {AST.CSS.Rule} rule
223
- * @param {AST.Element} element
270
+ * @param {any} element
224
271
  * @param {Direction} direction
225
272
  * @returns {boolean}
226
273
  */
@@ -271,12 +318,12 @@ function apply_selector(relative_selectors, rule, element, direction) {
271
318
  }
272
319
 
273
320
  /**
274
- * @param {AST.Element} node
321
+ * @param {any} node
275
322
  * @param {boolean} adjacent_only
276
- * @returns {AST.Element[]}
323
+ * @returns {any[]}
277
324
  */
278
325
  function get_ancestor_elements(node, adjacent_only) {
279
- /** @type {AST.Element[]} */
326
+ /** @type {any[]} */
280
327
  const ancestors = [];
281
328
 
282
329
  const path = node.metadata.path;
@@ -285,7 +332,7 @@ function get_ancestor_elements(node, adjacent_only) {
285
332
  while (i--) {
286
333
  const parent = path[i];
287
334
 
288
- if (parent.type === 'Element') {
335
+ if (is_native_jsx_element(parent)) {
289
336
  ancestors.push(parent);
290
337
  if (adjacent_only) {
291
338
  break;
@@ -297,12 +344,12 @@ function get_ancestor_elements(node, adjacent_only) {
297
344
  }
298
345
 
299
346
  /**
300
- * @param {AST.Element} node
347
+ * @param {any} node
301
348
  * @param {boolean} adjacent_only
302
- * @returns {AST.Element[]}
349
+ * @returns {any[]}
303
350
  */
304
351
  function get_descendant_elements(node, adjacent_only) {
305
- /** @type {AST.Element[]} */
352
+ /** @type {any[]} */
306
353
  const descendants = [];
307
354
 
308
355
  /**
@@ -311,26 +358,23 @@ function get_descendant_elements(node, adjacent_only) {
311
358
  * @returns {void}
312
359
  */
313
360
  function visit(current_node, depth = 0) {
314
- if (current_node.type === 'Element' && current_node !== node) {
361
+ if (is_native_jsx_element(current_node) && current_node !== node) {
315
362
  descendants.push(current_node);
316
363
  if (adjacent_only) return; // Only direct children for '>' combinator
317
364
  }
318
365
 
319
- // Visit children based on TSRX's template AST structure
320
- if (/** @type {AST.Element} */ (current_node).children) {
321
- for (const child of /** @type {AST.Element} */ (current_node).children) {
366
+ if (Array.isArray(/** @type {any} */ (current_node).children)) {
367
+ for (const child of /** @type {any} */ (current_node).children) {
322
368
  visit(child, depth + 1);
323
369
  }
324
370
  }
325
371
 
326
- // For template nodes and interpolation expressions
327
372
  if (
328
- (current_node.type === 'TSRXExpression' || current_node.type === 'Text') &&
329
- /** @type {AST.TSRXExpression | AST.TextNode} */ (current_node).expression &&
330
- typeof (/** @type {AST.TSRXExpression | AST.TextNode} */ (current_node).expression) ===
331
- 'object'
373
+ current_node.type === 'JSXExpressionContainer' &&
374
+ current_node.expression &&
375
+ typeof current_node.expression === 'object'
332
376
  ) {
333
- visit(/** @type {AST.TSRXExpression | AST.TextNode} */ (current_node).expression, depth + 1);
377
+ visit(current_node.expression, depth + 1);
334
378
  }
335
379
  }
336
380
 
@@ -357,18 +401,19 @@ function can_render_dynamic_content(element, check_classes = false) {
357
401
 
358
402
  // Either a dynamic element or component (only can tell at runtime)
359
403
  // But dynamic elements should return false ideally
360
- if (is_element_dynamic(/** @type {AST.Element} */ (element))) {
404
+ if (is_element_dynamic(element)) {
361
405
  return true;
362
406
  }
363
407
 
364
408
  // Check for dynamic class attributes if requested (for class-based selectors)
365
- if (check_classes && /** @type {AST.Element} */ (element).attributes) {
366
- for (const attr of /** @type {AST.Element} */ (element).attributes) {
367
- if (attr.type === 'Attribute' && attr.name.name === 'class') {
409
+ if (check_classes) {
410
+ for (const attr of get_element_attributes(element)) {
411
+ if (attr.type === 'JSXAttribute' && get_attribute_name(attr) === 'class') {
412
+ const value = get_attribute_value(attr);
368
413
  // Check if class value is an expression (not a static string)
369
- if (attr.value && typeof attr.value === 'object') {
414
+ if (value && typeof value === 'object') {
370
415
  // If it's a CallExpression or other dynamic value, it's dynamic
371
- if (attr.value.type !== 'Literal' && attr.value.type !== 'Text') {
416
+ if (value.type !== 'Literal') {
372
417
  return true;
373
418
  }
374
419
  }
@@ -383,7 +428,7 @@ function can_render_dynamic_content(element, check_classes = false) {
383
428
  * @param {AST.Node} node
384
429
  * @param {Direction} direction
385
430
  * @param {boolean} adjacent_only
386
- * @returns {Map<AST.Element, boolean>}
431
+ * @returns {Map<any, boolean>}
387
432
  */
388
433
  function get_possible_element_siblings(node, direction, adjacent_only) {
389
434
  const siblings = new Map();
@@ -415,7 +460,7 @@ function get_possible_element_siblings(node, direction, adjacent_only) {
415
460
  for (let i = start; i !== end; i += step) {
416
461
  const sibling = container[i];
417
462
 
418
- if (sibling.type === 'Element') {
463
+ if (is_native_jsx_element(sibling)) {
419
464
  siblings.set(sibling, true);
420
465
  // Don't break for dynamic elements (children and dynamic components)
421
466
  // as they can render dynamic content or might render nothing
@@ -425,13 +470,7 @@ function get_possible_element_siblings(node, direction, adjacent_only) {
425
470
  }
426
471
  }
427
472
  // Stop at non-whitespace text nodes for adjacent selectors
428
- else if (
429
- adjacent_only &&
430
- (sibling.type === 'TSRXExpression' || sibling.type === 'Text') &&
431
- sibling.expression.type === 'Literal' &&
432
- typeof sibling.expression.value === 'string' &&
433
- sibling.expression.value.trim()
434
- ) {
473
+ else if (adjacent_only && sibling.type === 'JSXText' && sibling.value.trim()) {
435
474
  break;
436
475
  }
437
476
  }
@@ -443,7 +482,7 @@ function get_possible_element_siblings(node, direction, adjacent_only) {
443
482
  * @param {AST.CSS.RelativeSelector} relative_selector
444
483
  * @param {AST.CSS.RelativeSelector[]} rest_selectors
445
484
  * @param {AST.CSS.Rule} rule
446
- * @param {AST.Element} node
485
+ * @param {any} node
447
486
  * @param {Direction} direction
448
487
  * @returns {boolean}
449
488
  */
@@ -515,7 +554,7 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
515
554
 
516
555
  for (let i = search_start; i < search_end; i++) {
517
556
  const subsequent = container[i];
518
- if (subsequent.type === 'Element') {
557
+ if (is_native_jsx_element(subsequent)) {
519
558
  if (apply_selector(remaining, rule, subsequent, direction)) {
520
559
  sibling_matched = true;
521
560
  break;
@@ -529,7 +568,7 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
529
568
  }
530
569
  // Don't apply_selector for dynamic elements - they won't match regular element selectors
531
570
  } else if (
532
- possible_sibling.type === 'Element' &&
571
+ is_native_jsx_element(possible_sibling) &&
533
572
  apply_selector(rest_selectors, rule, possible_sibling, direction)
534
573
  ) {
535
574
  sibling_matched = true;
@@ -551,7 +590,7 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
551
590
  }
552
591
  /**
553
592
  * @param {AST.Node} node
554
- * @returns {AST.Element | null}
593
+ * @returns {any | null}
555
594
  */
556
595
  function get_element_parent(node) {
557
596
  // Check if metadata and path exist
@@ -565,7 +604,7 @@ function get_element_parent(node) {
565
604
  while (i--) {
566
605
  const parent = path[i];
567
606
 
568
- if (parent.type === 'Element') {
607
+ if (is_native_jsx_element(parent)) {
569
608
  return parent;
570
609
  }
571
610
  }
@@ -664,11 +703,12 @@ function is_global(selector, rule) {
664
703
  }
665
704
 
666
705
  /**
667
- * @param {AST.Attribute} attribute
668
- * @returns {attribute is AST.Attribute & { value: AST.Literal & { value: string } }}
706
+ * @param {any} attribute
707
+ * @returns {boolean}
669
708
  */
670
709
  function is_text_attribute(attribute) {
671
- return attribute.value?.type === 'Literal' && typeof attribute.value.value === 'string';
710
+ const value = get_attribute_value(attribute);
711
+ return value?.type === 'Literal' && typeof value.value === 'string';
672
712
  }
673
713
 
674
714
  /**
@@ -702,7 +742,7 @@ function test_attribute(operator, expected_value, case_insensitive, value) {
702
742
  }
703
743
 
704
744
  /**
705
- * @param {AST.Element} node
745
+ * @param {any} node
706
746
  * @param {string} name
707
747
  * @param {string | null} expected_value
708
748
  * @param {string | null} operator
@@ -710,18 +750,29 @@ function test_attribute(operator, expected_value, case_insensitive, value) {
710
750
  * @returns {boolean}
711
751
  */
712
752
  function attribute_matches(node, name, expected_value, operator, case_insensitive) {
713
- for (const attribute of node.attributes) {
714
- if (attribute.type === 'SpreadAttribute') return true;
753
+ for (const attribute of get_element_attributes(node)) {
754
+ if (attribute.type === 'JSXSpreadAttribute') return true;
715
755
 
716
- if (attribute.type !== 'Attribute') continue;
756
+ if (attribute.type !== 'JSXAttribute') continue;
717
757
 
718
758
  const lowerCaseName = name.toLowerCase();
719
- if (![lowerCaseName, `$${lowerCaseName}`].includes(attribute.name.name.toLowerCase())) continue;
759
+ const attributeName = get_attribute_name(attribute);
760
+ if (
761
+ !attributeName ||
762
+ ![lowerCaseName, `$${lowerCaseName}`].includes(attributeName.toLowerCase())
763
+ ) {
764
+ continue;
765
+ }
720
766
 
721
767
  if (expected_value === null) return true;
722
768
 
723
769
  if (is_text_attribute(attribute)) {
724
- return test_attribute(operator, expected_value, case_insensitive, attribute.value.value);
770
+ return test_attribute(
771
+ operator,
772
+ expected_value,
773
+ case_insensitive,
774
+ get_attribute_value(attribute).value,
775
+ );
725
776
  } else {
726
777
  return true;
727
778
  }
@@ -754,7 +805,7 @@ function is_outer_global(relative_selector) {
754
805
  /**
755
806
  * @param {AST.CSS.RelativeSelector} relative_selector
756
807
  * @param {AST.CSS.Rule} rule
757
- * @param {AST.Element} element
808
+ * @param {any} element
758
809
  * @param {Direction} direction
759
810
  * @return {boolean}
760
811
  */
@@ -879,7 +930,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
879
930
  selector.metadata.scoped = true;
880
931
  }
881
932
 
882
- /** @type {AST.Element | null} */
933
+ /** @type {any | null} */
883
934
  let el = element;
884
935
  while (el) {
885
936
  el.metadata.scoped = true;
@@ -929,9 +980,11 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
929
980
  }
930
981
 
931
982
  case 'AttributeSelector': {
932
- const whitelisted = whitelist_attribute_selector.get(
933
- /** @type {AST.Identifier} */ (element.id).name.toLowerCase(),
934
- );
983
+ const element_name = get_element_name(element);
984
+ const whitelisted =
985
+ element_name?.type === 'Identifier' || element_name?.type === 'JSXIdentifier'
986
+ ? whitelist_attribute_selector.get(element_name.name.toLowerCase())
987
+ : undefined;
935
988
  if (
936
989
  !whitelisted?.includes(selector.name.toLowerCase()) &&
937
990
  !attribute_matches(
@@ -968,13 +1021,14 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
968
1021
  }
969
1022
 
970
1023
  case 'TypeSelector': {
971
- if (is_element_dynamic(/** @type {AST.Element} */ (element))) {
1024
+ if (is_element_dynamic(element)) {
972
1025
  break;
973
1026
  }
974
1027
 
1028
+ const element_name = get_element_name(element);
975
1029
  if (
976
- element.id.type === 'Identifier' &&
977
- element.id.name.toLowerCase() !== name.toLowerCase() &&
1030
+ (element_name?.type === 'Identifier' || element_name?.type === 'JSXIdentifier') &&
1031
+ element_name.name.toLowerCase() !== name.toLowerCase() &&
978
1032
  name !== '*'
979
1033
  ) {
980
1034
  return false;
@@ -1063,7 +1117,7 @@ function rule_has_animation(rule) {
1063
1117
 
1064
1118
  /**
1065
1119
  * @param {AST.CSS.StyleSheet} css
1066
- * @param {AST.Element} element
1120
+ * @param {any} element
1067
1121
  * @param {StyleClasses} styleClasses
1068
1122
  * @param {TopScopedClasses} topScopedClasses
1069
1123
  * @return {void}
@@ -1073,6 +1127,10 @@ export function prune_css(css, element, styleClasses, topScopedClasses) {
1073
1127
  style_identifier_classes = styleClasses;
1074
1128
  top_scoped_classes = topScopedClasses;
1075
1129
 
1130
+ if (is_element_dynamic(element)) {
1131
+ element.metadata.scoped = true;
1132
+ }
1133
+
1076
1134
  /** @type {Visitors<AST.CSS.Node, null>} */
1077
1135
  const visitors = {
1078
1136
  Rule(node, context) {
@@ -9,9 +9,17 @@ import { DIAGNOSTIC_CODES } from '../diagnostics.js';
9
9
  export const TSRX_RETURN_STATEMENT_ERROR =
10
10
  'Return statements are not allowed inside TSRX templates. Move the return before the TSRX return value, or use conditional rendering instead.';
11
11
  export const TSRX_LOOP_RETURN_ERROR =
12
- 'Return statements are not allowed inside TSRX template for...of loops. Use continue instead.';
12
+ 'Return statements are not allowed inside TSRX template for...of loops. Filter the iterable before rendering or use an @for empty fallback for empty lists.';
13
13
  export const TSRX_LOOP_BREAK_ERROR =
14
14
  'Break statements are not allowed inside TSRX template for...of loops.';
15
+ export const TSRX_LOOP_CONTINUE_ERROR =
16
+ 'Continue statements are not allowed inside TSRX template for...of loops. Filter the iterable before rendering.';
17
+ export const TSRX_IF_RETURN_ERROR =
18
+ 'Return statements are not allowed inside TSRX template @if blocks. Move the return before the template output or render conditionally instead.';
19
+ export const TSRX_IF_BREAK_ERROR =
20
+ 'Break statements are not allowed inside TSRX template @if blocks.';
21
+ export const TSRX_IF_CONTINUE_ERROR =
22
+ 'Continue statements are not allowed inside TSRX template @if blocks. Filter before rendering or use conditional output instead.';
15
23
  export const TSRX_FOR_STATEMENT_ERROR =
16
24
  'For loops are not supported in TSRX templates. Use for...of instead.';
17
25
  export const TSRX_FOR_IN_STATEMENT_ERROR =
@@ -134,11 +142,12 @@ const invalid_nestings = {
134
142
  };
135
143
 
136
144
  /**
137
- * @param {AST.Element} element
145
+ * @param {any} element
138
146
  * @returns {string | null}
139
147
  */
140
148
  function get_element_tag(element) {
141
- return element.id.type === 'Identifier' ? element.id.name : null;
149
+ const name = element.openingElement?.name ?? element.id;
150
+ return name?.type === 'JSXIdentifier' || name?.type === 'Identifier' ? name.name : null;
142
151
  }
143
152
 
144
153
  /**
@@ -218,6 +227,64 @@ export function validate_tsrx_loop_break_statement(node, filename, errors, comme
218
227
  );
219
228
  }
220
229
 
230
+ /**
231
+ * @param {AST.ContinueStatement} node
232
+ * @param {string | null | undefined} filename
233
+ * @param {CompileError[]} [errors]
234
+ * @param {AST.CommentWithLocation[]} [comments]
235
+ */
236
+ export function validate_tsrx_loop_continue_statement(node, filename, errors, comments) {
237
+ error(
238
+ TSRX_LOOP_CONTINUE_ERROR,
239
+ filename ?? null,
240
+ get_statement_keyword_node(node, 'continue'),
241
+ errors,
242
+ comments,
243
+ );
244
+ }
245
+
246
+ /**
247
+ * @param {AST.ReturnStatement} node
248
+ * @param {string | null | undefined} filename
249
+ * @param {CompileError[]} [errors]
250
+ * @param {AST.CommentWithLocation[]} [comments]
251
+ */
252
+ export function validate_tsrx_if_return_statement(node, filename, errors, comments) {
253
+ error(TSRX_IF_RETURN_ERROR, filename ?? null, get_return_keyword_node(node), errors, comments);
254
+ }
255
+
256
+ /**
257
+ * @param {AST.BreakStatement} node
258
+ * @param {string | null | undefined} filename
259
+ * @param {CompileError[]} [errors]
260
+ * @param {AST.CommentWithLocation[]} [comments]
261
+ */
262
+ export function validate_tsrx_if_break_statement(node, filename, errors, comments) {
263
+ error(
264
+ TSRX_IF_BREAK_ERROR,
265
+ filename ?? null,
266
+ get_statement_keyword_node(node, 'break'),
267
+ errors,
268
+ comments,
269
+ );
270
+ }
271
+
272
+ /**
273
+ * @param {AST.ContinueStatement} node
274
+ * @param {string | null | undefined} filename
275
+ * @param {CompileError[]} [errors]
276
+ * @param {AST.CommentWithLocation[]} [comments]
277
+ */
278
+ export function validate_tsrx_if_continue_statement(node, filename, errors, comments) {
279
+ error(
280
+ TSRX_IF_CONTINUE_ERROR,
281
+ filename ?? null,
282
+ get_statement_keyword_node(node, 'continue'),
283
+ errors,
284
+ comments,
285
+ );
286
+ }
287
+
221
288
  /**
222
289
  * @param {AST.ForStatement | AST.ForInStatement | AST.WhileStatement | AST.DoWhileStatement} node
223
290
  * @param {string | null | undefined} filename
@@ -240,7 +307,57 @@ export function validate_tsrx_unsupported_loop_statement(node, filename, errors,
240
307
  }
241
308
 
242
309
  /**
243
- * @param {AST.Element} element
310
+ * Returns `true` when `child` occupies a value slot of `parent` — i.e. it is
311
+ * being captured as a value (assigned to a binding, pushed into an array,
312
+ * passed as an argument, used as an operand, …) rather than rendered as a
313
+ * statement-position template child.
314
+ *
315
+ * Target analyzers use this to tell apart direct template output from a TSRX
316
+ * element that merely happens to be a value, so that a value-position element
317
+ * nested inside plain JavaScript control flow does not get mistaken for direct
318
+ * output that would require a `@for`/`@if`/`@switch`/`@try` directive.
319
+ * @param {AST.Node} parent
320
+ * @param {AST.Node} child
321
+ * @returns {boolean}
322
+ */
323
+ export function is_template_value_position(parent, child) {
324
+ switch (parent.type) {
325
+ case 'VariableDeclarator':
326
+ return parent.init === child;
327
+ case 'AssignmentExpression':
328
+ return parent.right === child;
329
+ case 'Property':
330
+ case 'PropertyDefinition':
331
+ return parent.value === child;
332
+ case 'ArrayExpression':
333
+ return /** @type {any[]} */ (parent.elements).includes(child);
334
+ case 'CallExpression':
335
+ case 'NewExpression':
336
+ return parent.callee === child || /** @type {any[]} */ (parent.arguments).includes(child);
337
+ case 'ConditionalExpression':
338
+ return parent.test === child || parent.consequent === child || parent.alternate === child;
339
+ case 'LogicalExpression':
340
+ case 'BinaryExpression':
341
+ return parent.left === child || parent.right === child;
342
+ case 'UnaryExpression':
343
+ case 'AwaitExpression':
344
+ case 'SpreadElement':
345
+ case 'YieldExpression':
346
+ return parent.argument === child;
347
+ case 'TemplateLiteral':
348
+ case 'SequenceExpression':
349
+ return /** @type {any[]} */ (parent.expressions).includes(child);
350
+ case 'TSAsExpression':
351
+ case 'TSNonNullExpression':
352
+ case 'TSSatisfiesExpression':
353
+ return parent.expression === child;
354
+ default:
355
+ return false;
356
+ }
357
+ }
358
+
359
+ /**
360
+ * @param {any} element
244
361
  * @param {AnalysisContext} context
245
362
  * @param {CompileError[]} [errors]
246
363
  */
@@ -253,7 +370,7 @@ export function validate_nesting(element, context, errors) {
253
370
 
254
371
  for (let i = context.path.length - 1; i >= 0; i--) {
255
372
  const parent = context.path[i];
256
- if (parent.type === 'Element') {
373
+ if (parent.type === 'JSXElement' || parent.type === 'JSXStyleElement') {
257
374
  const parent_tag = get_element_tag(parent);
258
375
  if (parent_tag === null) {
259
376
  continue;
package/src/index.js CHANGED
@@ -9,7 +9,6 @@
9
9
  export { parse_module as parseModule } from './parse/parse-module.js';
10
10
  export {
11
11
  get_comment_handlers as getCommentHandlers,
12
- convert_from_jsx as convertFromJsx,
13
12
  skipWhitespace,
14
13
  isWhitespaceTextNode,
15
14
  BINDING_TYPES,
@@ -181,7 +180,6 @@ export {
181
180
  create_compile_error,
182
181
  create_generated_identifier,
183
182
  create_null_literal,
184
- expand_switch_cases_for_fallthrough,
185
183
  flatten_switch_consequent,
186
184
  get_for_of_iteration_params,
187
185
  identifier_to_jsx_name,
@@ -189,6 +187,7 @@ export {
189
187
  is_component_jsx_name,
190
188
  is_dynamic_element_id,
191
189
  is_jsx_child,
190
+ jsx_name_to_expression,
192
191
  set_loc,
193
192
  to_text_expression,
194
193
  } from './transform/jsx/ast-builders.js';
@@ -199,7 +198,7 @@ export {
199
198
  export {
200
199
  prepare_stylesheet_for_render as prepareStylesheetForRender,
201
200
  is_style_element as isStyleElement,
202
- is_composite_element as isCompositeElement,
201
+ is_composite_jsx_element as isCompositeElement,
203
202
  annotate_with_hash as annotateWithHash,
204
203
  annotate_component_with_hash as annotateComponentWithHash,
205
204
  add_hash_class as addHashClass,
@@ -241,15 +240,24 @@ export {
241
240
  TSRX_DO_WHILE_STATEMENT_ERROR,
242
241
  TSRX_FOR_IN_STATEMENT_ERROR,
243
242
  TSRX_FOR_STATEMENT_ERROR,
243
+ TSRX_IF_BREAK_ERROR,
244
+ TSRX_IF_CONTINUE_ERROR,
245
+ TSRX_IF_RETURN_ERROR,
244
246
  TSRX_LOOP_BREAK_ERROR,
247
+ TSRX_LOOP_CONTINUE_ERROR,
245
248
  TSRX_LOOP_RETURN_ERROR,
246
249
  TSRX_RETURN_STATEMENT_ERROR,
247
250
  TSRX_WHILE_STATEMENT_ERROR,
248
251
  get_return_keyword_node as getReturnKeywordNode,
249
252
  get_statement_keyword_node as getStatementKeywordNode,
253
+ validate_tsrx_if_break_statement as validateTsrxIfBreakStatement,
254
+ validate_tsrx_if_continue_statement as validateTsrxIfContinueStatement,
255
+ validate_tsrx_if_return_statement as validateTsrxIfReturnStatement,
250
256
  validate_tsrx_loop_break_statement as validateTsrxLoopBreakStatement,
257
+ validate_tsrx_loop_continue_statement as validateTsrxLoopContinueStatement,
251
258
  validate_tsrx_loop_return_statement as validateTsrxLoopReturnStatement,
252
259
  validate_tsrx_return_statement as validateTsrxReturnStatement,
253
260
  validate_tsrx_unsupported_loop_statement as validateTsrxUnsupportedLoopStatement,
254
261
  validate_nesting as validateNesting,
262
+ is_template_value_position as isTemplateValuePosition,
255
263
  } from './analyze/validation.js';