@tsrx/core 0.1.66 → 0.1.68
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.
|
|
6
|
+
"version": "0.1.68",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"esrap": "^2.3.2",
|
|
81
81
|
"magic-string": "^0.30.18",
|
|
82
82
|
"zimmerframe": "^1.1.2",
|
|
83
|
-
"@tsrx/runtime": "0.1.
|
|
83
|
+
"@tsrx/runtime": "0.1.6"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@types/node": "^24.3.0",
|
package/src/analyze/prune.js
CHANGED
|
@@ -422,18 +422,14 @@ function can_render_dynamic_content(element, check_classes = false) {
|
|
|
422
422
|
function get_possible_element_siblings(node, direction, adjacent_only) {
|
|
423
423
|
/** @type {Map<AST.TSRXJSXElement, boolean>} */
|
|
424
424
|
const siblings = new Map();
|
|
425
|
-
const
|
|
425
|
+
const container = get_sibling_container(node);
|
|
426
426
|
|
|
427
|
-
if (
|
|
427
|
+
if (container === null) {
|
|
428
428
|
return siblings;
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
-
// Get the container that holds the siblings
|
|
432
|
-
const container = node_children(parent);
|
|
433
431
|
const node_index = container.indexOf(node);
|
|
434
432
|
|
|
435
|
-
if (node_index === -1) return siblings;
|
|
436
|
-
|
|
437
433
|
// Determine which siblings to check based on direction
|
|
438
434
|
let start, end, step;
|
|
439
435
|
if (direction === FORWARD) {
|
|
@@ -532,9 +528,8 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
|
|
|
532
528
|
sibling_matched = true;
|
|
533
529
|
} else {
|
|
534
530
|
// Check if there are any elements after this component that could match the remaining selectors
|
|
535
|
-
const
|
|
536
|
-
if (
|
|
537
|
-
const container = node_children(parent);
|
|
531
|
+
const container = get_sibling_container(node);
|
|
532
|
+
if (container !== null) {
|
|
538
533
|
const component_index = container.indexOf(possible_sibling);
|
|
539
534
|
|
|
540
535
|
// For adjacent combinator, only check immediate next element
|
|
@@ -578,6 +573,38 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
|
|
|
578
573
|
return true;
|
|
579
574
|
}
|
|
580
575
|
}
|
|
576
|
+
/**
|
|
577
|
+
* The children list an element sits in — that of the nearest ancestor on its
|
|
578
|
+
* path whose children include it, which is its parent element, a fragment
|
|
579
|
+
* (an authored `<>…</>`, a control-flow branch's output, or the synthetic
|
|
580
|
+
* root of a style scope), or `null` when the element has no container.
|
|
581
|
+
*
|
|
582
|
+
* Sibling combinators (`+`, `~`) read this list. Ancestor combinators keep
|
|
583
|
+
* using {@link get_element_parent}: a fragment is not an element, and a
|
|
584
|
+
* scope's root fragment stands for the container that a scoped block never
|
|
585
|
+
* styles.
|
|
586
|
+
*
|
|
587
|
+
* @param {AST.TSRXElementNode} node
|
|
588
|
+
* @returns {AST.Node[] | null}
|
|
589
|
+
*/
|
|
590
|
+
function get_sibling_container(node) {
|
|
591
|
+
const path = node.metadata?.path;
|
|
592
|
+
if (!path || !path.length) {
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
let i = path.length;
|
|
597
|
+
|
|
598
|
+
while (i--) {
|
|
599
|
+
const children = node_children(path[i]);
|
|
600
|
+
if (children.includes(node)) {
|
|
601
|
+
return children;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
|
|
581
608
|
/**
|
|
582
609
|
* @param {AST.TSRXElementNode} node
|
|
583
610
|
* @returns {AST.TSRXJSXElement | null}
|
package/src/index.js
CHANGED
|
@@ -252,6 +252,7 @@ export {
|
|
|
252
252
|
// Analyze
|
|
253
253
|
export { analyze_css as analyzeCss } from './analyze/css-analyze.js';
|
|
254
254
|
export { prune_css as pruneCss } from './analyze/prune.js';
|
|
255
|
+
export { create_scope_root as createScopeRoot } from './transform/jsx/style-scopes.js';
|
|
255
256
|
export {
|
|
256
257
|
TSRX_DO_WHILE_STATEMENT_ERROR,
|
|
257
258
|
TSRX_FORGOTTEN_STATEMENT_CONTAINER_ERROR,
|
|
@@ -446,7 +446,9 @@ function prepare_scope(own, render_items, holder, state) {
|
|
|
446
446
|
}
|
|
447
447
|
const hash = sheets.length > 0 ? sheets[0][1].hash : null;
|
|
448
448
|
const refs = collect_style_ref_attributes(own);
|
|
449
|
-
const elements = collect_css_prunable_elements(render_items, [], ctx
|
|
449
|
+
const elements = collect_css_prunable_elements(render_items, [], ctx, [
|
|
450
|
+
create_scope_root(render_items),
|
|
451
|
+
]);
|
|
450
452
|
|
|
451
453
|
/** @type {AST.CSS.StyleSheet | null} */
|
|
452
454
|
let first_sheet = null;
|
|
@@ -566,12 +568,34 @@ export function apply_css_definition_metadata(
|
|
|
566
568
|
}
|
|
567
569
|
}
|
|
568
570
|
|
|
571
|
+
/**
|
|
572
|
+
* The root of a scope's ancestor paths: a fragment holding the list's items,
|
|
573
|
+
* so `prune_css` can find the siblings of a top-level item for `+` and `~`
|
|
574
|
+
* combinators. It is not an element, so ancestor combinators never match it —
|
|
575
|
+
* the container of a scope is exactly what a scoped block does not style.
|
|
576
|
+
*
|
|
577
|
+
* @param {AST.Node[]} items
|
|
578
|
+
* @returns {AST.Node}
|
|
579
|
+
*/
|
|
580
|
+
export function create_scope_root(items) {
|
|
581
|
+
return /** @type {AST.Node} */ (
|
|
582
|
+
/** @type {unknown} */ ({
|
|
583
|
+
type: 'JSXFragment',
|
|
584
|
+
openingFragment: { type: 'JSXOpeningFragment' },
|
|
585
|
+
closingFragment: { type: 'JSXClosingFragment' },
|
|
586
|
+
children: items,
|
|
587
|
+
metadata: { path: [] },
|
|
588
|
+
})
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
|
|
569
592
|
/**
|
|
570
593
|
* Pruning runs before the walker stamps paths onto template nodes, so each
|
|
571
594
|
* collected element gets its ancestor chain (`metadata.path`) here —
|
|
572
595
|
* descendant/sibling selector matching in `prune_css` reads it. The scope's
|
|
573
596
|
* own elements and those of nested scopes are collected; a component
|
|
574
|
-
* boundary stops the walk.
|
|
597
|
+
* boundary stops the walk. Callers that collect a scope's items themselves
|
|
598
|
+
* should seed `path` with {@link create_scope_root} for the same reason.
|
|
575
599
|
*
|
|
576
600
|
* @param {AST.Node | AST.Node[]} value
|
|
577
601
|
* @param {AST.TSRXJSXElement[]} [elements]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"elements": {
|
|
3
|
+
"a scope": ["scope"],
|
|
4
|
+
"b": ["scope"],
|
|
5
|
+
"c": ["scope"],
|
|
6
|
+
"wrap": ["scope"],
|
|
7
|
+
"x": ["scope"],
|
|
8
|
+
"y": ["scope"],
|
|
9
|
+
"d": ["scope"],
|
|
10
|
+
"e": ["scope"],
|
|
11
|
+
"g": ["scope"]
|
|
12
|
+
},
|
|
13
|
+
"cssOrder": ["scope"],
|
|
14
|
+
"pruned": [".f + .g"],
|
|
15
|
+
"classMaps": {}
|
|
16
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Sibling combinators between the items of one scope, and between the items
|
|
2
|
+
// of a branch fragment: both siblings carry the scope hash, so the rules are
|
|
3
|
+
// kept. Only the descendant chain through `.wrap` had a parent element to find
|
|
4
|
+
// siblings in before; the others sit at the top of their list.
|
|
5
|
+
export function SiblingCombinators({ open }: { open: boolean }) @{
|
|
6
|
+
<>
|
|
7
|
+
<style>
|
|
8
|
+
.scope {
|
|
9
|
+
--label: scope;
|
|
10
|
+
}
|
|
11
|
+
.a + .b {
|
|
12
|
+
color: red;
|
|
13
|
+
}
|
|
14
|
+
.a ~ .c {
|
|
15
|
+
color: blue;
|
|
16
|
+
}
|
|
17
|
+
.wrap .x + .y {
|
|
18
|
+
color: green;
|
|
19
|
+
}
|
|
20
|
+
.d + .e {
|
|
21
|
+
color: black;
|
|
22
|
+
}
|
|
23
|
+
.f + .g {
|
|
24
|
+
color: gray;
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
27
|
+
<div class="a scope">{'a'}</div>
|
|
28
|
+
<div class="b">{'b'}</div>
|
|
29
|
+
<div class="c">{'c'}</div>
|
|
30
|
+
<div class="wrap">
|
|
31
|
+
<span class="x">{'x'}</span>
|
|
32
|
+
<span class="y">{'y'}</span>
|
|
33
|
+
</div>
|
|
34
|
+
@if (open) {
|
|
35
|
+
<>
|
|
36
|
+
<i class="d">{'d'}</i>
|
|
37
|
+
<i class="e">{'e'}</i>
|
|
38
|
+
</>
|
|
39
|
+
}
|
|
40
|
+
<i class="g">{'g'}</i>
|
|
41
|
+
</>
|
|
42
|
+
}
|