@tsrx/core 0.1.49 → 0.1.51

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.49",
6
+ "version": "0.1.51",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
@@ -30,6 +30,13 @@
30
30
  "./types/helpers": {
31
31
  "types": "./types/helpers.d.ts"
32
32
  },
33
+ "./types/vite/dep-scan": {
34
+ "types": "./types/vite/dep-scan.d.ts"
35
+ },
36
+ "./vite/dep-scan": {
37
+ "types": "./src/vite/dep-scan.js",
38
+ "default": "./src/vite/dep-scan.js"
39
+ },
33
40
  "./runtime/ref": {
34
41
  "types": "./types/runtime/ref.d.ts",
35
42
  "default": "./src/runtime/ref.js"
@@ -56,6 +63,7 @@
56
63
  },
57
64
  "./test-harness/source-mappings": "./tests/shared/source-mappings.js",
58
65
  "./test-harness/compile": "./tests/shared/compile.js",
66
+ "./test-harness/dep-scan": "./tests/shared/dep-scan.js",
59
67
  "./test-harness/runtime/*": "./tests/shared/runtime/*.js",
60
68
  "./test-harness/runtime/*.js": "./tests/shared/runtime/*.js",
61
69
  "./test-harness/runtime/*.tsrx": "./tests/shared/runtime/*.tsrx"
@@ -77,6 +85,7 @@
77
85
  "@typescript-eslint/types": "^8.40.0",
78
86
  "@volar/language-core": "~2.4.28",
79
87
  "typescript": "^5.9.3",
88
+ "vite": "^8.1.5",
80
89
  "vscode-languageserver-types": "^3.17.5"
81
90
  },
82
91
  "files": [
@@ -1,14 +1,14 @@
1
1
  /** @import * as AST from 'estree' */
2
2
  /** @import * as ESTreeJSX from 'estree-jsx' */
3
- /** @import { Visitors, TopScopedClasses, StyleClasses } from '../../types/index' */
4
- /** @typedef {0 | 1} Direction */
3
+ /** @import { CssPruneDirection, Visitors, TopScopedClasses, StyleClasses } from '../../types/index' */
5
4
 
6
5
  import { walk } from 'zimmerframe';
6
+ import { node_children } from '../utils/ast.js';
7
7
 
8
8
  const regex_backslash_and_following_character = /\\(.)/g;
9
- /** @type {Direction} */
9
+ /** @type {CssPruneDirection} */
10
10
  const FORWARD = 0;
11
- /** @type {Direction} */
11
+ /** @type {CssPruneDirection} */
12
12
  const BACKWARD = 1;
13
13
 
14
14
  // this will be set for every prune_css call
@@ -21,42 +21,41 @@ let style_identifier_classes;
21
21
  let top_scoped_classes;
22
22
 
23
23
  /**
24
- * @param {any} node
25
- * @returns {boolean}
24
+ * @param {AST.Node | null | undefined} node
25
+ * @returns {node is AST.TSRXJSXElement}
26
26
  */
27
27
  function is_native_jsx_element(node) {
28
- return node?.type === 'JSXElement' && node.metadata?.native_tsrx;
28
+ return node?.type === 'JSXElement' && node.metadata?.native_tsrx === true;
29
29
  }
30
30
 
31
31
  /**
32
- * @param {any} node
33
- * @returns {any}
32
+ * @param {AST.TSRXElementNode} node
33
+ * @returns {ESTreeJSX.TSRXJSXOpeningElement['name']}
34
34
  */
35
35
  function get_element_name(node) {
36
- return node.openingElement?.name ?? node.id;
36
+ return node.openingElement.name;
37
37
  }
38
38
 
39
39
  /**
40
- * @param {any} node
41
- * @returns {any[]}
40
+ * @param {AST.TSRXElementNode} node
41
+ * @returns {ESTreeJSX.JSXAttributeNode[]}
42
42
  */
43
43
  function get_element_attributes(node) {
44
- return node.openingElement?.attributes ?? node.attributes ?? [];
44
+ return node.openingElement.attributes;
45
45
  }
46
46
 
47
47
  /**
48
- * @param {any} attribute
48
+ * @param {ESTreeJSX.JSXAttribute} attribute
49
49
  * @returns {string | null}
50
50
  */
51
51
  function get_attribute_name(attribute) {
52
52
  const name = attribute.name;
53
- if (name?.type === 'JSXIdentifier' || name?.type === 'Identifier') return name.name;
54
- return null;
53
+ return name.type === 'JSXIdentifier' ? name.name : null;
55
54
  }
56
55
 
57
56
  /**
58
- * @param {any} attribute
59
- * @returns {any}
57
+ * @param {ESTreeJSX.JSXAttribute} attribute
58
+ * @returns {AST.Expression | ESTreeJSX.JSXEmptyExpression | null}
60
59
  */
61
60
  function get_attribute_value(attribute) {
62
61
  const value = attribute.value;
@@ -64,32 +63,27 @@ function get_attribute_value(attribute) {
64
63
  }
65
64
 
66
65
  /**
67
- * @param {AST.Node} node
66
+ * @param {AST.TSRXElementNode} node
68
67
  * @returns {boolean}
69
68
  */
70
69
  function is_dynamic_element(node) {
71
70
  // `metadata.dynamicElement` marks lowered dynamic tags; `isDynamic` is the
72
71
  // parser flag on a not-yet-lowered `<{expr}>` element. Both resolve their
73
72
  // tag at runtime, so they can match any type selector.
74
- return (
75
- node?.metadata?.dynamicElement === true ||
76
- /** @type {ESTreeJSX.JSXExpressionContainer} */ (node)?.isDynamic === true
77
- );
73
+ return node.metadata?.dynamicElement === true || node.isDynamic === true;
78
74
  }
79
75
 
80
76
  /**
81
77
  * Returns true if node is a DOM element (not a component).
82
- * @param {AST.Node} node
78
+ * @param {AST.TSRXElementNode} node
83
79
  * @returns {boolean}
84
80
  */
85
81
  function is_element_dom_element(node) {
86
82
  const id = get_element_name(node);
87
- return (
88
- (id.type === 'Identifier' || id.type === 'JSXIdentifier') &&
89
- id.name[0].toLowerCase() === id.name[0] &&
90
- id.name !== 'children' &&
91
- !id.tracked
92
- );
83
+ if (id.type !== 'Identifier' && id.type !== 'JSXIdentifier') return false;
84
+ if (id.name[0].toLowerCase() !== id.name[0] || id.name === 'children') return false;
85
+ // Only a plain `Identifier` tag can carry the tracked (`@name`) marker.
86
+ return id.type !== 'Identifier' || !id.tracked;
93
87
  }
94
88
 
95
89
  // CSS selector constants
@@ -269,8 +263,8 @@ function truncate(node) {
269
263
  /**
270
264
  * @param {AST.CSS.RelativeSelector[]} relative_selectors
271
265
  * @param {AST.CSS.Rule} rule
272
- * @param {any} element
273
- * @param {Direction} direction
266
+ * @param {AST.TSRXElementNode} element
267
+ * @param {CssPruneDirection} direction
274
268
  * @returns {boolean}
275
269
  */
276
270
  function apply_selector(relative_selectors, rule, element, direction) {
@@ -320,12 +314,12 @@ function apply_selector(relative_selectors, rule, element, direction) {
320
314
  }
321
315
 
322
316
  /**
323
- * @param {any} node
317
+ * @param {AST.TSRXElementNode} node
324
318
  * @param {boolean} adjacent_only
325
- * @returns {any[]}
319
+ * @returns {AST.TSRXJSXElement[]}
326
320
  */
327
321
  function get_ancestor_elements(node, adjacent_only) {
328
- /** @type {any[]} */
322
+ /** @type {AST.TSRXJSXElement[]} */
329
323
  const ancestors = [];
330
324
 
331
325
  const path = node.metadata.path;
@@ -346,12 +340,12 @@ function get_ancestor_elements(node, adjacent_only) {
346
340
  }
347
341
 
348
342
  /**
349
- * @param {any} node
343
+ * @param {AST.TSRXElementNode} node
350
344
  * @param {boolean} adjacent_only
351
- * @returns {any[]}
345
+ * @returns {AST.TSRXJSXElement[]}
352
346
  */
353
347
  function get_descendant_elements(node, adjacent_only) {
354
- /** @type {any[]} */
348
+ /** @type {AST.TSRXJSXElement[]} */
355
349
  const descendants = [];
356
350
 
357
351
  /**
@@ -365,26 +359,18 @@ function get_descendant_elements(node, adjacent_only) {
365
359
  if (adjacent_only) return; // Only direct children for '>' combinator
366
360
  }
367
361
 
368
- if (Array.isArray(/** @type {any} */ (current_node).children)) {
369
- for (const child of /** @type {any} */ (current_node).children) {
370
- visit(child, depth + 1);
371
- }
362
+ for (const child of node_children(current_node)) {
363
+ visit(child, depth + 1);
372
364
  }
373
365
 
374
- if (
375
- current_node.type === 'JSXExpressionContainer' &&
376
- current_node.expression &&
377
- typeof current_node.expression === 'object'
378
- ) {
366
+ if (current_node.type === 'JSXExpressionContainer') {
379
367
  visit(current_node.expression, depth + 1);
380
368
  }
381
369
  }
382
370
 
383
371
  // Start from node's children
384
- if (node.children) {
385
- for (const child of node.children) {
386
- visit(child);
387
- }
372
+ for (const child of node_children(node)) {
373
+ visit(child);
388
374
  }
389
375
 
390
376
  return descendants;
@@ -392,7 +378,7 @@ function get_descendant_elements(node, adjacent_only) {
392
378
 
393
379
  /**
394
380
  * Check if an element can render dynamic content that might affect CSS matching
395
- * @param {AST.Node} element
381
+ * @param {AST.TSRXElementNode} element
396
382
  * @param {boolean} check_classes - Whether to check for dynamic class attributes
397
383
  * @returns {boolean}
398
384
  */
@@ -425,12 +411,13 @@ function can_render_dynamic_content(element, check_classes = false) {
425
411
  }
426
412
 
427
413
  /**
428
- * @param {AST.Node} node
429
- * @param {Direction} direction
414
+ * @param {AST.TSRXElementNode} node
415
+ * @param {CssPruneDirection} direction
430
416
  * @param {boolean} adjacent_only
431
- * @returns {Map<any, boolean>}
417
+ * @returns {Map<AST.TSRXJSXElement, boolean>}
432
418
  */
433
419
  function get_possible_element_siblings(node, direction, adjacent_only) {
420
+ /** @type {Map<AST.TSRXJSXElement, boolean>} */
434
421
  const siblings = new Map();
435
422
  const parent = get_element_parent(node);
436
423
 
@@ -439,7 +426,7 @@ function get_possible_element_siblings(node, direction, adjacent_only) {
439
426
  }
440
427
 
441
428
  // Get the container that holds the siblings
442
- const container = parent.children || [];
429
+ const container = node_children(parent);
443
430
  const node_index = container.indexOf(node);
444
431
 
445
432
  if (node_index === -1) return siblings;
@@ -482,8 +469,8 @@ function get_possible_element_siblings(node, direction, adjacent_only) {
482
469
  * @param {AST.CSS.RelativeSelector} relative_selector
483
470
  * @param {AST.CSS.RelativeSelector[]} rest_selectors
484
471
  * @param {AST.CSS.Rule} rule
485
- * @param {any} node
486
- * @param {Direction} direction
472
+ * @param {AST.TSRXElementNode} node
473
+ * @param {CssPruneDirection} direction
487
474
  * @returns {boolean}
488
475
  */
489
476
  function apply_combinator(relative_selector, rest_selectors, rule, node, direction) {
@@ -544,7 +531,7 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
544
531
  // Check if there are any elements after this component that could match the remaining selectors
545
532
  const parent = get_element_parent(node);
546
533
  if (parent) {
547
- const container = parent.children || [];
534
+ const container = node_children(parent);
548
535
  const component_index = container.indexOf(possible_sibling);
549
536
 
550
537
  // For adjacent combinator, only check immediate next element
@@ -589,8 +576,8 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
589
576
  }
590
577
  }
591
578
  /**
592
- * @param {AST.Node} node
593
- * @returns {any | null}
579
+ * @param {AST.TSRXElementNode} node
580
+ * @returns {AST.TSRXJSXElement | null}
594
581
  */
595
582
  function get_element_parent(node) {
596
583
  // Check if metadata and path exist
@@ -703,12 +690,14 @@ function is_global(selector, rule) {
703
690
  }
704
691
 
705
692
  /**
706
- * @param {any} attribute
707
- * @returns {boolean}
693
+ * The attribute's value when it is a static string literal, otherwise `null`.
694
+ *
695
+ * @param {ESTreeJSX.JSXAttribute} attribute
696
+ * @returns {string | null}
708
697
  */
709
- function is_text_attribute(attribute) {
698
+ function get_text_attribute_value(attribute) {
710
699
  const value = get_attribute_value(attribute);
711
- return value?.type === 'Literal' && typeof value.value === 'string';
700
+ return value?.type === 'Literal' && typeof value.value === 'string' ? value.value : null;
712
701
  }
713
702
 
714
703
  /**
@@ -742,7 +731,7 @@ function test_attribute(operator, expected_value, case_insensitive, value) {
742
731
  }
743
732
 
744
733
  /**
745
- * @param {any} node
734
+ * @param {AST.TSRXElementNode} node
746
735
  * @param {string} name
747
736
  * @param {string | null} expected_value
748
737
  * @param {string | null} operator
@@ -768,13 +757,9 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv
768
757
 
769
758
  if (expected_value === null) return true;
770
759
 
771
- if (is_text_attribute(attribute)) {
772
- return test_attribute(
773
- operator,
774
- expected_value,
775
- case_insensitive,
776
- get_attribute_value(attribute).value,
777
- );
760
+ const text_value = get_text_attribute_value(attribute);
761
+ if (text_value !== null) {
762
+ return test_attribute(operator, expected_value, case_insensitive, text_value);
778
763
  } else {
779
764
  return true;
780
765
  }
@@ -807,8 +792,8 @@ function is_outer_global(relative_selector) {
807
792
  /**
808
793
  * @param {AST.CSS.RelativeSelector} relative_selector
809
794
  * @param {AST.CSS.Rule} rule
810
- * @param {any} element
811
- * @param {Direction} direction
795
+ * @param {AST.TSRXElementNode} element
796
+ * @param {CssPruneDirection} direction
812
797
  * @return {boolean}
813
798
  */
814
799
  function relative_selector_might_apply_to_node(relative_selector, rule, element, direction) {
@@ -932,7 +917,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
932
917
  selector.metadata.scoped = true;
933
918
  }
934
919
 
935
- /** @type {any | null} */
920
+ /** @type {AST.TSRXElementNode | null} */
936
921
  let el = element;
937
922
  while (el) {
938
923
  el.metadata.scoped = true;
@@ -1119,7 +1104,7 @@ function rule_has_animation(rule) {
1119
1104
 
1120
1105
  /**
1121
1106
  * @param {AST.CSS.StyleSheet} css
1122
- * @param {any} element
1107
+ * @param {AST.TSRXElementNode} element
1123
1108
  * @param {StyleClasses} styleClasses
1124
1109
  * @param {TopScopedClasses} topScopedClasses
1125
1110
  * @return {void}
@@ -144,12 +144,12 @@ const invalid_nestings = {
144
144
  };
145
145
 
146
146
  /**
147
- * @param {any} element
147
+ * @param {AST.TSRXElementNode} element
148
148
  * @returns {string | null}
149
149
  */
150
150
  function get_element_tag(element) {
151
- const name = element.openingElement?.name ?? element.id;
152
- return name?.type === 'JSXIdentifier' || name?.type === 'Identifier' ? name.name : null;
151
+ const name = element.openingElement.name;
152
+ return name.type === 'JSXIdentifier' || name.type === 'Identifier' ? name.name : null;
153
153
  }
154
154
 
155
155
  /**
@@ -349,10 +349,10 @@ export function is_template_value_position(parent, child) {
349
349
  case 'PropertyDefinition':
350
350
  return parent.value === child;
351
351
  case 'ArrayExpression':
352
- return /** @type {any[]} */ (parent.elements).includes(child);
352
+ return parent.elements.some((element) => element === child);
353
353
  case 'CallExpression':
354
354
  case 'NewExpression':
355
- return parent.callee === child || /** @type {any[]} */ (parent.arguments).includes(child);
355
+ return parent.callee === child || parent.arguments.some((argument) => argument === child);
356
356
  case 'ConditionalExpression':
357
357
  return parent.test === child || parent.consequent === child || parent.alternate === child;
358
358
  case 'LogicalExpression':
@@ -365,7 +365,7 @@ export function is_template_value_position(parent, child) {
365
365
  return parent.argument === child;
366
366
  case 'TemplateLiteral':
367
367
  case 'SequenceExpression':
368
- return /** @type {any[]} */ (parent.expressions).includes(child);
368
+ return parent.expressions.some((expression) => expression === child);
369
369
  case 'TSAsExpression':
370
370
  case 'TSNonNullExpression':
371
371
  case 'TSSatisfiesExpression':
@@ -376,7 +376,7 @@ export function is_template_value_position(parent, child) {
376
376
  }
377
377
 
378
378
  /**
379
- * @param {any} element
379
+ * @param {AST.TSRXElementNode} element
380
380
  * @param {AnalysisContext} context
381
381
  * @param {CompileError[]} [errors]
382
382
  */