@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 +10 -1
- package/src/analyze/prune.js +62 -77
- package/src/analyze/validation.js +7 -7
- package/src/parse/index.js +59 -80
- package/src/plugin.js +219 -107
- package/src/runtime/language-helpers.js +34 -25
- package/src/runtime/ref.js +57 -36
- package/src/scope.js +4 -5
- package/src/source-map-utils.js +8 -22
- package/src/transform/imports.js +8 -14
- package/src/transform/jsx/ast-builders.js +4 -1
- package/src/transform/jsx/index.js +1 -13
- package/src/transform/jsx/server-module.js +102 -96
- package/src/transform/jsx-hoist.js +4 -4
- package/src/transform/lazy.js +196 -137
- package/src/transform/scoping.js +108 -100
- package/src/transform/segments.js +24 -49
- package/src/transform/style-ref.js +92 -106
- package/src/transform/stylesheet.js +5 -21
- package/src/utils/ast.js +25 -20
- package/src/utils/builders.js +66 -2
- package/src/vite/dep-scan.js +135 -0
- package/types/index.d.ts +317 -17
- package/types/parse.d.ts +98 -13
- package/types/runtime/language-helpers.d.ts +10 -5
- package/types/runtime/ref.d.ts +63 -14
- package/types/vite/dep-scan.d.ts +26 -0
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.
|
|
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": [
|
package/src/analyze/prune.js
CHANGED
|
@@ -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 {
|
|
9
|
+
/** @type {CssPruneDirection} */
|
|
10
10
|
const FORWARD = 0;
|
|
11
|
-
/** @type {
|
|
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 {
|
|
25
|
-
* @returns {
|
|
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 {
|
|
33
|
-
* @returns {
|
|
32
|
+
* @param {AST.TSRXElementNode} node
|
|
33
|
+
* @returns {ESTreeJSX.TSRXJSXOpeningElement['name']}
|
|
34
34
|
*/
|
|
35
35
|
function get_element_name(node) {
|
|
36
|
-
return node.openingElement
|
|
36
|
+
return node.openingElement.name;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* @param {
|
|
41
|
-
* @returns {
|
|
40
|
+
* @param {AST.TSRXElementNode} node
|
|
41
|
+
* @returns {ESTreeJSX.JSXAttributeNode[]}
|
|
42
42
|
*/
|
|
43
43
|
function get_element_attributes(node) {
|
|
44
|
-
return node.openingElement
|
|
44
|
+
return node.openingElement.attributes;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
* @param {
|
|
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
|
-
|
|
54
|
-
return null;
|
|
53
|
+
return name.type === 'JSXIdentifier' ? name.name : null;
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
/**
|
|
58
|
-
* @param {
|
|
59
|
-
* @returns {
|
|
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.
|
|
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.
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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 {
|
|
273
|
-
* @param {
|
|
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 {
|
|
317
|
+
* @param {AST.TSRXElementNode} node
|
|
324
318
|
* @param {boolean} adjacent_only
|
|
325
|
-
* @returns {
|
|
319
|
+
* @returns {AST.TSRXJSXElement[]}
|
|
326
320
|
*/
|
|
327
321
|
function get_ancestor_elements(node, adjacent_only) {
|
|
328
|
-
/** @type {
|
|
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 {
|
|
343
|
+
* @param {AST.TSRXElementNode} node
|
|
350
344
|
* @param {boolean} adjacent_only
|
|
351
|
-
* @returns {
|
|
345
|
+
* @returns {AST.TSRXJSXElement[]}
|
|
352
346
|
*/
|
|
353
347
|
function get_descendant_elements(node, adjacent_only) {
|
|
354
|
-
/** @type {
|
|
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
|
-
|
|
369
|
-
|
|
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
|
-
|
|
385
|
-
|
|
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.
|
|
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.
|
|
429
|
-
* @param {
|
|
414
|
+
* @param {AST.TSRXElementNode} node
|
|
415
|
+
* @param {CssPruneDirection} direction
|
|
430
416
|
* @param {boolean} adjacent_only
|
|
431
|
-
* @returns {Map<
|
|
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
|
|
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 {
|
|
486
|
-
* @param {
|
|
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
|
|
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.
|
|
593
|
-
* @returns {
|
|
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
|
-
*
|
|
707
|
-
*
|
|
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
|
|
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 {
|
|
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
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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 {
|
|
811
|
-
* @param {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
147
|
+
* @param {AST.TSRXElementNode} element
|
|
148
148
|
* @returns {string | null}
|
|
149
149
|
*/
|
|
150
150
|
function get_element_tag(element) {
|
|
151
|
-
const name = element.openingElement
|
|
152
|
-
return name
|
|
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
|
|
352
|
+
return parent.elements.some((element) => element === child);
|
|
353
353
|
case 'CallExpression':
|
|
354
354
|
case 'NewExpression':
|
|
355
|
-
return parent.callee === 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
|
|
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 {
|
|
379
|
+
* @param {AST.TSRXElementNode} element
|
|
380
380
|
* @param {AnalysisContext} context
|
|
381
381
|
* @param {CompileError[]} [errors]
|
|
382
382
|
*/
|