@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 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.20",
6
+ "version": "0.1.24",
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,30 +19,72 @@ 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
+
65
+ /**
66
+ * @param {AST.Node} node
67
+ * @returns {boolean}
68
+ */
69
+ function is_runtime_dynamic_element(node) {
70
+ return node?.metadata?.runtime_dynamic_element === true;
71
+ }
72
+
22
73
  /**
23
74
  * Returns true if node is a DOM element (not a component).
24
75
  * @param {AST.Node} node
25
76
  * @returns {boolean}
26
77
  */
27
78
  function is_element_dom_element(node) {
28
- const id = /** @type {AST.Element} */ (node).id;
79
+ const id = get_element_name(node);
29
80
  return (
30
- id.type === 'Identifier' &&
81
+ (id.type === 'Identifier' || id.type === 'JSXIdentifier') &&
31
82
  id.name[0].toLowerCase() === id.name[0] &&
32
83
  id.name !== 'children' &&
33
84
  !id.tracked
34
85
  );
35
86
  }
36
87
 
37
- /**
38
- * Returns true if element is dynamic.
39
- * @param {AST.Element} node
40
- * @returns {boolean}
41
- */
42
- function is_element_dynamic(node) {
43
- return node.id.type === 'Identifier' ? !!node.id.tracked : false;
44
- }
45
-
46
88
  // CSS selector constants
47
89
  /**
48
90
  * @param {number} start
@@ -220,7 +262,7 @@ function truncate(node) {
220
262
  /**
221
263
  * @param {AST.CSS.RelativeSelector[]} relative_selectors
222
264
  * @param {AST.CSS.Rule} rule
223
- * @param {AST.Element} element
265
+ * @param {any} element
224
266
  * @param {Direction} direction
225
267
  * @returns {boolean}
226
268
  */
@@ -271,12 +313,12 @@ function apply_selector(relative_selectors, rule, element, direction) {
271
313
  }
272
314
 
273
315
  /**
274
- * @param {AST.Element} node
316
+ * @param {any} node
275
317
  * @param {boolean} adjacent_only
276
- * @returns {AST.Element[]}
318
+ * @returns {any[]}
277
319
  */
278
320
  function get_ancestor_elements(node, adjacent_only) {
279
- /** @type {AST.Element[]} */
321
+ /** @type {any[]} */
280
322
  const ancestors = [];
281
323
 
282
324
  const path = node.metadata.path;
@@ -285,7 +327,7 @@ function get_ancestor_elements(node, adjacent_only) {
285
327
  while (i--) {
286
328
  const parent = path[i];
287
329
 
288
- if (parent.type === 'Element') {
330
+ if (is_native_jsx_element(parent)) {
289
331
  ancestors.push(parent);
290
332
  if (adjacent_only) {
291
333
  break;
@@ -297,12 +339,12 @@ function get_ancestor_elements(node, adjacent_only) {
297
339
  }
298
340
 
299
341
  /**
300
- * @param {AST.Element} node
342
+ * @param {any} node
301
343
  * @param {boolean} adjacent_only
302
- * @returns {AST.Element[]}
344
+ * @returns {any[]}
303
345
  */
304
346
  function get_descendant_elements(node, adjacent_only) {
305
- /** @type {AST.Element[]} */
347
+ /** @type {any[]} */
306
348
  const descendants = [];
307
349
 
308
350
  /**
@@ -311,26 +353,23 @@ function get_descendant_elements(node, adjacent_only) {
311
353
  * @returns {void}
312
354
  */
313
355
  function visit(current_node, depth = 0) {
314
- if (current_node.type === 'Element' && current_node !== node) {
356
+ if (is_native_jsx_element(current_node) && current_node !== node) {
315
357
  descendants.push(current_node);
316
358
  if (adjacent_only) return; // Only direct children for '>' combinator
317
359
  }
318
360
 
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) {
361
+ if (Array.isArray(/** @type {any} */ (current_node).children)) {
362
+ for (const child of /** @type {any} */ (current_node).children) {
322
363
  visit(child, depth + 1);
323
364
  }
324
365
  }
325
366
 
326
- // For template nodes and interpolation expressions
327
367
  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'
368
+ current_node.type === 'JSXExpressionContainer' &&
369
+ current_node.expression &&
370
+ typeof current_node.expression === 'object'
332
371
  ) {
333
- visit(/** @type {AST.TSRXExpression | AST.TextNode} */ (current_node).expression, depth + 1);
372
+ visit(current_node.expression, depth + 1);
334
373
  }
335
374
  }
336
375
 
@@ -351,24 +390,23 @@ function get_descendant_elements(node, adjacent_only) {
351
390
  * @returns {boolean}
352
391
  */
353
392
  function can_render_dynamic_content(element, check_classes = false) {
354
- if (!is_element_dom_element(element)) {
393
+ if (is_runtime_dynamic_element(element)) {
355
394
  return true;
356
395
  }
357
396
 
358
- // Either a dynamic element or component (only can tell at runtime)
359
- // But dynamic elements should return false ideally
360
- if (is_element_dynamic(/** @type {AST.Element} */ (element))) {
397
+ if (!is_element_dom_element(element)) {
361
398
  return true;
362
399
  }
363
400
 
364
401
  // 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') {
402
+ if (check_classes) {
403
+ for (const attr of get_element_attributes(element)) {
404
+ if (attr.type === 'JSXAttribute' && get_attribute_name(attr) === 'class') {
405
+ const value = get_attribute_value(attr);
368
406
  // Check if class value is an expression (not a static string)
369
- if (attr.value && typeof attr.value === 'object') {
407
+ if (value && typeof value === 'object') {
370
408
  // If it's a CallExpression or other dynamic value, it's dynamic
371
- if (attr.value.type !== 'Literal' && attr.value.type !== 'Text') {
409
+ if (value.type !== 'Literal') {
372
410
  return true;
373
411
  }
374
412
  }
@@ -383,7 +421,7 @@ function can_render_dynamic_content(element, check_classes = false) {
383
421
  * @param {AST.Node} node
384
422
  * @param {Direction} direction
385
423
  * @param {boolean} adjacent_only
386
- * @returns {Map<AST.Element, boolean>}
424
+ * @returns {Map<any, boolean>}
387
425
  */
388
426
  function get_possible_element_siblings(node, direction, adjacent_only) {
389
427
  const siblings = new Map();
@@ -415,7 +453,7 @@ function get_possible_element_siblings(node, direction, adjacent_only) {
415
453
  for (let i = start; i !== end; i += step) {
416
454
  const sibling = container[i];
417
455
 
418
- if (sibling.type === 'Element') {
456
+ if (is_native_jsx_element(sibling)) {
419
457
  siblings.set(sibling, true);
420
458
  // Don't break for dynamic elements (children and dynamic components)
421
459
  // as they can render dynamic content or might render nothing
@@ -425,13 +463,7 @@ function get_possible_element_siblings(node, direction, adjacent_only) {
425
463
  }
426
464
  }
427
465
  // 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
- ) {
466
+ else if (adjacent_only && sibling.type === 'JSXText' && sibling.value.trim()) {
435
467
  break;
436
468
  }
437
469
  }
@@ -443,7 +475,7 @@ function get_possible_element_siblings(node, direction, adjacent_only) {
443
475
  * @param {AST.CSS.RelativeSelector} relative_selector
444
476
  * @param {AST.CSS.RelativeSelector[]} rest_selectors
445
477
  * @param {AST.CSS.Rule} rule
446
- * @param {AST.Element} node
478
+ * @param {any} node
447
479
  * @param {Direction} direction
448
480
  * @returns {boolean}
449
481
  */
@@ -515,7 +547,7 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
515
547
 
516
548
  for (let i = search_start; i < search_end; i++) {
517
549
  const subsequent = container[i];
518
- if (subsequent.type === 'Element') {
550
+ if (is_native_jsx_element(subsequent)) {
519
551
  if (apply_selector(remaining, rule, subsequent, direction)) {
520
552
  sibling_matched = true;
521
553
  break;
@@ -529,7 +561,7 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
529
561
  }
530
562
  // Don't apply_selector for dynamic elements - they won't match regular element selectors
531
563
  } else if (
532
- possible_sibling.type === 'Element' &&
564
+ is_native_jsx_element(possible_sibling) &&
533
565
  apply_selector(rest_selectors, rule, possible_sibling, direction)
534
566
  ) {
535
567
  sibling_matched = true;
@@ -551,7 +583,7 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
551
583
  }
552
584
  /**
553
585
  * @param {AST.Node} node
554
- * @returns {AST.Element | null}
586
+ * @returns {any | null}
555
587
  */
556
588
  function get_element_parent(node) {
557
589
  // Check if metadata and path exist
@@ -565,7 +597,7 @@ function get_element_parent(node) {
565
597
  while (i--) {
566
598
  const parent = path[i];
567
599
 
568
- if (parent.type === 'Element') {
600
+ if (is_native_jsx_element(parent)) {
569
601
  return parent;
570
602
  }
571
603
  }
@@ -664,11 +696,12 @@ function is_global(selector, rule) {
664
696
  }
665
697
 
666
698
  /**
667
- * @param {AST.Attribute} attribute
668
- * @returns {attribute is AST.Attribute & { value: AST.Literal & { value: string } }}
699
+ * @param {any} attribute
700
+ * @returns {boolean}
669
701
  */
670
702
  function is_text_attribute(attribute) {
671
- return attribute.value?.type === 'Literal' && typeof attribute.value.value === 'string';
703
+ const value = get_attribute_value(attribute);
704
+ return value?.type === 'Literal' && typeof value.value === 'string';
672
705
  }
673
706
 
674
707
  /**
@@ -702,7 +735,7 @@ function test_attribute(operator, expected_value, case_insensitive, value) {
702
735
  }
703
736
 
704
737
  /**
705
- * @param {AST.Element} node
738
+ * @param {any} node
706
739
  * @param {string} name
707
740
  * @param {string | null} expected_value
708
741
  * @param {string | null} operator
@@ -710,18 +743,29 @@ function test_attribute(operator, expected_value, case_insensitive, value) {
710
743
  * @returns {boolean}
711
744
  */
712
745
  function attribute_matches(node, name, expected_value, operator, case_insensitive) {
713
- for (const attribute of node.attributes) {
714
- if (attribute.type === 'SpreadAttribute') return true;
746
+ for (const attribute of get_element_attributes(node)) {
747
+ if (attribute.type === 'JSXSpreadAttribute') return true;
715
748
 
716
- if (attribute.type !== 'Attribute') continue;
749
+ if (attribute.type !== 'JSXAttribute') continue;
717
750
 
718
751
  const lowerCaseName = name.toLowerCase();
719
- if (![lowerCaseName, `$${lowerCaseName}`].includes(attribute.name.name.toLowerCase())) continue;
752
+ const attributeName = get_attribute_name(attribute);
753
+ if (
754
+ !attributeName ||
755
+ ![lowerCaseName, `$${lowerCaseName}`].includes(attributeName.toLowerCase())
756
+ ) {
757
+ continue;
758
+ }
720
759
 
721
760
  if (expected_value === null) return true;
722
761
 
723
762
  if (is_text_attribute(attribute)) {
724
- return test_attribute(operator, expected_value, case_insensitive, attribute.value.value);
763
+ return test_attribute(
764
+ operator,
765
+ expected_value,
766
+ case_insensitive,
767
+ get_attribute_value(attribute).value,
768
+ );
725
769
  } else {
726
770
  return true;
727
771
  }
@@ -754,7 +798,7 @@ function is_outer_global(relative_selector) {
754
798
  /**
755
799
  * @param {AST.CSS.RelativeSelector} relative_selector
756
800
  * @param {AST.CSS.Rule} rule
757
- * @param {AST.Element} element
801
+ * @param {any} element
758
802
  * @param {Direction} direction
759
803
  * @return {boolean}
760
804
  */
@@ -879,7 +923,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
879
923
  selector.metadata.scoped = true;
880
924
  }
881
925
 
882
- /** @type {AST.Element | null} */
926
+ /** @type {any | null} */
883
927
  let el = element;
884
928
  while (el) {
885
929
  el.metadata.scoped = true;
@@ -929,9 +973,11 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
929
973
  }
930
974
 
931
975
  case 'AttributeSelector': {
932
- const whitelisted = whitelist_attribute_selector.get(
933
- /** @type {AST.Identifier} */ (element.id).name.toLowerCase(),
934
- );
976
+ const element_name = get_element_name(element);
977
+ const whitelisted =
978
+ element_name?.type === 'Identifier' || element_name?.type === 'JSXIdentifier'
979
+ ? whitelist_attribute_selector.get(element_name.name.toLowerCase())
980
+ : undefined;
935
981
  if (
936
982
  !whitelisted?.includes(selector.name.toLowerCase()) &&
937
983
  !attribute_matches(
@@ -968,13 +1014,14 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
968
1014
  }
969
1015
 
970
1016
  case 'TypeSelector': {
971
- if (is_element_dynamic(/** @type {AST.Element} */ (element))) {
1017
+ if (is_runtime_dynamic_element(element)) {
972
1018
  break;
973
1019
  }
974
1020
 
1021
+ const element_name = get_element_name(element);
975
1022
  if (
976
- element.id.type === 'Identifier' &&
977
- element.id.name.toLowerCase() !== name.toLowerCase() &&
1023
+ (element_name?.type === 'Identifier' || element_name?.type === 'JSXIdentifier') &&
1024
+ element_name.name.toLowerCase() !== name.toLowerCase() &&
978
1025
  name !== '*'
979
1026
  ) {
980
1027
  return false;
@@ -1063,7 +1110,7 @@ function rule_has_animation(rule) {
1063
1110
 
1064
1111
  /**
1065
1112
  * @param {AST.CSS.StyleSheet} css
1066
- * @param {AST.Element} element
1113
+ * @param {any} element
1067
1114
  * @param {StyleClasses} styleClasses
1068
1115
  * @param {TopScopedClasses} topScopedClasses
1069
1116
  * @return {void}
@@ -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 @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;
@@ -4,4 +4,5 @@ export const DIAGNOSTIC_CODES = {
4
4
  MISMATCHED_CLOSING_TAG: 'tsrx-mismatched-closing-tag',
5
5
  TEMPLATE_EXPRESSION_TRAILING_SEMICOLON: 'tsrx-template-expression-trailing-semicolon',
6
6
  TEMPLATE_RETURN_STATEMENT: 'tsrx-template-return-statement',
7
+ FORGOTTEN_STATEMENT_CONTAINER: 'tsrx-forgotten-statement-container',
7
8
  };
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,13 +180,11 @@ 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,
188
186
  is_bare_render_expression,
189
187
  is_component_jsx_name,
190
- is_dynamic_element_id,
191
188
  is_jsx_child,
192
189
  set_loc,
193
190
  to_text_expression,
@@ -199,7 +196,7 @@ export {
199
196
  export {
200
197
  prepare_stylesheet_for_render as prepareStylesheetForRender,
201
198
  is_style_element as isStyleElement,
202
- is_composite_element as isCompositeElement,
199
+ is_composite_jsx_element as isCompositeElement,
203
200
  annotate_with_hash as annotateWithHash,
204
201
  annotate_component_with_hash as annotateComponentWithHash,
205
202
  add_hash_class as addHashClass,
@@ -241,15 +238,24 @@ export {
241
238
  TSRX_DO_WHILE_STATEMENT_ERROR,
242
239
  TSRX_FOR_IN_STATEMENT_ERROR,
243
240
  TSRX_FOR_STATEMENT_ERROR,
241
+ TSRX_IF_BREAK_ERROR,
242
+ TSRX_IF_CONTINUE_ERROR,
243
+ TSRX_IF_RETURN_ERROR,
244
244
  TSRX_LOOP_BREAK_ERROR,
245
+ TSRX_LOOP_CONTINUE_ERROR,
245
246
  TSRX_LOOP_RETURN_ERROR,
246
247
  TSRX_RETURN_STATEMENT_ERROR,
247
248
  TSRX_WHILE_STATEMENT_ERROR,
248
249
  get_return_keyword_node as getReturnKeywordNode,
249
250
  get_statement_keyword_node as getStatementKeywordNode,
251
+ validate_tsrx_if_break_statement as validateTsrxIfBreakStatement,
252
+ validate_tsrx_if_continue_statement as validateTsrxIfContinueStatement,
253
+ validate_tsrx_if_return_statement as validateTsrxIfReturnStatement,
250
254
  validate_tsrx_loop_break_statement as validateTsrxLoopBreakStatement,
255
+ validate_tsrx_loop_continue_statement as validateTsrxLoopContinueStatement,
251
256
  validate_tsrx_loop_return_statement as validateTsrxLoopReturnStatement,
252
257
  validate_tsrx_return_statement as validateTsrxReturnStatement,
253
258
  validate_tsrx_unsupported_loop_statement as validateTsrxUnsupportedLoopStatement,
254
259
  validate_nesting as validateNesting,
260
+ is_template_value_position as isTemplateValuePosition,
255
261
  } from './analyze/validation.js';