ripple 0.2.165 → 0.2.167
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 +2 -2
- package/src/compiler/phases/1-parse/index.js +30 -1
- package/src/compiler/phases/1-parse/style.js +36 -1
- package/src/compiler/phases/2-analyze/css-analyze.js +145 -0
- package/src/compiler/phases/2-analyze/index.js +7 -0
- package/src/compiler/phases/2-analyze/prune.js +165 -11
- package/src/compiler/phases/2-analyze/validation.js +156 -0
- package/src/compiler/phases/3-transform/client/index.js +62 -12
- package/src/compiler/phases/3-transform/stylesheet.js +102 -3
- package/src/runtime/internal/client/index.js +1 -0
- package/src/runtime/internal/client/operations.js +0 -6
- package/src/runtime/internal/client/render.js +22 -16
- package/tests/client/css/global-additional-cases.test.ripple +702 -0
- package/tests/client/css/global-advanced-selectors.test.ripple +229 -0
- package/tests/client/css/global-at-rules.test.ripple +126 -0
- package/tests/client/css/global-basic.test.ripple +165 -0
- package/tests/client/css/global-classes-ids.test.ripple +179 -0
- package/tests/client/css/global-combinators.test.ripple +124 -0
- package/tests/client/css/global-complex-nesting.test.ripple +221 -0
- package/tests/client/css/global-edge-cases.test.ripple +200 -0
- package/tests/client/css/global-keyframes.test.ripple +101 -0
- package/tests/client/css/global-nested.test.ripple +150 -0
- package/tests/client/css/global-pseudo.test.ripple +155 -0
- package/tests/client/css/global-scoping.test.ripple +229 -0
- package/tests/client/dynamic-elements.test.ripple +0 -1
- package/tests/server/streaming-ssr.test.ripple +9 -6
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { error } from '../../errors.js';
|
|
2
|
+
|
|
3
|
+
const invalid_nestings = {
|
|
4
|
+
// <p> cannot contain block-level elements
|
|
5
|
+
p: new Set([
|
|
6
|
+
'address',
|
|
7
|
+
'article',
|
|
8
|
+
'aside',
|
|
9
|
+
'blockquote',
|
|
10
|
+
'details',
|
|
11
|
+
'div',
|
|
12
|
+
'dl',
|
|
13
|
+
'fieldset',
|
|
14
|
+
'figcaption',
|
|
15
|
+
'figure',
|
|
16
|
+
'footer',
|
|
17
|
+
'form',
|
|
18
|
+
'h1',
|
|
19
|
+
'h2',
|
|
20
|
+
'h3',
|
|
21
|
+
'h4',
|
|
22
|
+
'h5',
|
|
23
|
+
'h6',
|
|
24
|
+
'header',
|
|
25
|
+
'hgroup',
|
|
26
|
+
'hr',
|
|
27
|
+
'main',
|
|
28
|
+
'menu',
|
|
29
|
+
'nav',
|
|
30
|
+
'ol',
|
|
31
|
+
'p',
|
|
32
|
+
'pre',
|
|
33
|
+
'section',
|
|
34
|
+
'table',
|
|
35
|
+
'ul',
|
|
36
|
+
]),
|
|
37
|
+
// <span> cannot contain block-level elements
|
|
38
|
+
span: new Set([
|
|
39
|
+
'address',
|
|
40
|
+
'article',
|
|
41
|
+
'aside',
|
|
42
|
+
'blockquote',
|
|
43
|
+
'details',
|
|
44
|
+
'div',
|
|
45
|
+
'dl',
|
|
46
|
+
'fieldset',
|
|
47
|
+
'figcaption',
|
|
48
|
+
'figure',
|
|
49
|
+
'footer',
|
|
50
|
+
'form',
|
|
51
|
+
'h1',
|
|
52
|
+
'h2',
|
|
53
|
+
'h3',
|
|
54
|
+
'h4',
|
|
55
|
+
'h5',
|
|
56
|
+
'h6',
|
|
57
|
+
'header',
|
|
58
|
+
'hgroup',
|
|
59
|
+
'hr',
|
|
60
|
+
'main',
|
|
61
|
+
'menu',
|
|
62
|
+
'nav',
|
|
63
|
+
'ol',
|
|
64
|
+
'p',
|
|
65
|
+
'pre',
|
|
66
|
+
'section',
|
|
67
|
+
'table',
|
|
68
|
+
'ul',
|
|
69
|
+
]),
|
|
70
|
+
// Interactive elements cannot be nested
|
|
71
|
+
a: new Set(['a', 'button']),
|
|
72
|
+
button: new Set(['a', 'button']),
|
|
73
|
+
// Form elements
|
|
74
|
+
label: new Set(['label']),
|
|
75
|
+
form: new Set(['form']),
|
|
76
|
+
// Headings cannot be nested within each other
|
|
77
|
+
h1: new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']),
|
|
78
|
+
h2: new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']),
|
|
79
|
+
h3: new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']),
|
|
80
|
+
h4: new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']),
|
|
81
|
+
h5: new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']),
|
|
82
|
+
h6: new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']),
|
|
83
|
+
// Table structure
|
|
84
|
+
table: new Set(['table', 'tr', 'td', 'th']), // Can only contain caption, colgroup, thead, tbody, tfoot
|
|
85
|
+
thead: new Set(['caption', 'colgroup', 'thead', 'tbody', 'tfoot', 'td', 'th']), // Can only contain tr
|
|
86
|
+
tbody: new Set(['caption', 'colgroup', 'thead', 'tbody', 'tfoot', 'td', 'th']), // Can only contain tr
|
|
87
|
+
tfoot: new Set(['caption', 'colgroup', 'thead', 'tbody', 'tfoot', 'td', 'th']), // Can only contain tr
|
|
88
|
+
tr: new Set(['caption', 'colgroup', 'thead', 'tbody', 'tfoot', 'tr']), // Can only contain td and th
|
|
89
|
+
td: new Set(['td', 'th']), // Cannot nest td/th elements
|
|
90
|
+
th: new Set(['td', 'th']), // Cannot nest td/th elements
|
|
91
|
+
// Media elements
|
|
92
|
+
picture: new Set(['picture']),
|
|
93
|
+
// Main landmark - only one per document, cannot be nested
|
|
94
|
+
main: new Set(['main']),
|
|
95
|
+
// Other semantic restrictions
|
|
96
|
+
figcaption: new Set(['figcaption']),
|
|
97
|
+
dt: new Set([
|
|
98
|
+
'header',
|
|
99
|
+
'footer',
|
|
100
|
+
'article',
|
|
101
|
+
'aside',
|
|
102
|
+
'nav',
|
|
103
|
+
'section',
|
|
104
|
+
'h1',
|
|
105
|
+
'h2',
|
|
106
|
+
'h3',
|
|
107
|
+
'h4',
|
|
108
|
+
'h5',
|
|
109
|
+
'h6',
|
|
110
|
+
]),
|
|
111
|
+
// No interactive content inside summary
|
|
112
|
+
summary: new Set(['summary']),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {any} element
|
|
117
|
+
* @returns {string | null}
|
|
118
|
+
*/
|
|
119
|
+
function get_element_tag(element) {
|
|
120
|
+
return element.id.type === 'Identifier' ? element.id.name : null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @param {any} element
|
|
125
|
+
* @param {any} state
|
|
126
|
+
* @param {any} context
|
|
127
|
+
*/
|
|
128
|
+
export function validate_nesting(element, state, context) {
|
|
129
|
+
const tag = get_element_tag(element);
|
|
130
|
+
|
|
131
|
+
if (tag === null) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (let i = context.path.length - 1; i >= 0; i--) {
|
|
136
|
+
const parent = context.path[i];
|
|
137
|
+
if (parent.type === 'Element') {
|
|
138
|
+
const parent_tag = get_element_tag(parent);
|
|
139
|
+
if (parent_tag === null) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (parent_tag in invalid_nestings) {
|
|
144
|
+
const validation_set =
|
|
145
|
+
invalid_nestings[/** @type {keyof typeof invalid_nestings} */ (parent_tag)];
|
|
146
|
+
if (validation_set.has(tag)) {
|
|
147
|
+
error(
|
|
148
|
+
`Invalid HTML nesting: <${tag}> cannot be a descendant of <${parent_tag}>.`,
|
|
149
|
+
state.analysis.module.filename,
|
|
150
|
+
context,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import {Expression, FunctionExpression, Node, Program} from 'estree' */
|
|
1
|
+
/** @import {Expression, FunctionExpression, Node, Program, Statement} from 'estree' */
|
|
2
2
|
|
|
3
3
|
/** @typedef {Map<number, {offset: number, delta: number}>} PostProcessingChanges */
|
|
4
4
|
/** @typedef {number[]} LineOffsets */
|
|
@@ -129,7 +129,7 @@ function visit_head_element(node, context) {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
function apply_updates(init, update) {
|
|
132
|
+
function apply_updates(init, update, state) {
|
|
133
133
|
if (update.length === 1) {
|
|
134
134
|
init.push(
|
|
135
135
|
b.stmt(
|
|
@@ -155,8 +155,25 @@ function apply_updates(init, update) {
|
|
|
155
155
|
const render_statements = [];
|
|
156
156
|
let index = 0;
|
|
157
157
|
|
|
158
|
+
const grouped_updates = new Map();
|
|
159
|
+
|
|
158
160
|
for (const u of update) {
|
|
159
161
|
if (u.initial) {
|
|
162
|
+
const id =
|
|
163
|
+
u.identity.type === 'Identifier' ? state.scope.get(u.identity.name)?.initial : u.identity;
|
|
164
|
+
let updates = grouped_updates.get(id);
|
|
165
|
+
|
|
166
|
+
if (updates === undefined) {
|
|
167
|
+
updates = [];
|
|
168
|
+
grouped_updates.set(id, updates);
|
|
169
|
+
}
|
|
170
|
+
updates.push(u);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const [, updates] of grouped_updates) {
|
|
175
|
+
if (updates.length === 1) {
|
|
176
|
+
const u = updates[0];
|
|
160
177
|
const key = index_to_key(index);
|
|
161
178
|
index_map.set(u.operation, key);
|
|
162
179
|
initial.push(b.prop('init', b.id(key), u.initial));
|
|
@@ -171,6 +188,29 @@ function apply_updates(init, update) {
|
|
|
171
188
|
);
|
|
172
189
|
index++;
|
|
173
190
|
} else {
|
|
191
|
+
const key = index_to_key(index);
|
|
192
|
+
/** @type {Array<Statement>} */
|
|
193
|
+
const if_body = [
|
|
194
|
+
b.stmt(b.assignment('=', b.member(b.id('__prev'), b.id(key)), b.id('__' + key))),
|
|
195
|
+
];
|
|
196
|
+
initial.push(b.prop('init', b.id(key), updates[0].initial));
|
|
197
|
+
render_statements.push(
|
|
198
|
+
b.var('__' + key, updates[0].expression),
|
|
199
|
+
b.if(
|
|
200
|
+
b.binary('!==', b.member(b.id('__prev'), b.id(key)), b.id('__' + key)),
|
|
201
|
+
b.block(if_body),
|
|
202
|
+
),
|
|
203
|
+
);
|
|
204
|
+
for (const u of updates) {
|
|
205
|
+
index_map.set(u.operation, key);
|
|
206
|
+
if_body.push(u.operation(b.id('__' + key)));
|
|
207
|
+
index++;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
for (const u of update) {
|
|
213
|
+
if (!u.initial) {
|
|
174
214
|
render_statements.push(u.operation);
|
|
175
215
|
}
|
|
176
216
|
}
|
|
@@ -835,6 +875,7 @@ const visitors = {
|
|
|
835
875
|
if (is_dom_element) {
|
|
836
876
|
let class_attribute = null;
|
|
837
877
|
let style_attribute = null;
|
|
878
|
+
/** @type {Array<Statement>} */
|
|
838
879
|
const local_updates = [];
|
|
839
880
|
const is_void = is_void_element(node.id.name);
|
|
840
881
|
|
|
@@ -997,9 +1038,11 @@ const visitors = {
|
|
|
997
1038
|
});
|
|
998
1039
|
} else {
|
|
999
1040
|
local_updates.push({
|
|
1000
|
-
operation:
|
|
1001
|
-
b.call('_$_.set_attribute', id, b.literal(attribute),
|
|
1002
|
-
|
|
1041
|
+
operation: (key) =>
|
|
1042
|
+
b.stmt(b.call('_$_.set_attribute', id, b.literal(attribute), key)),
|
|
1043
|
+
expression,
|
|
1044
|
+
identity: attr.value,
|
|
1045
|
+
initial: b.void0,
|
|
1003
1046
|
});
|
|
1004
1047
|
}
|
|
1005
1048
|
} else {
|
|
@@ -1044,6 +1087,7 @@ const visitors = {
|
|
|
1044
1087
|
operation: (key) =>
|
|
1045
1088
|
b.stmt(b.call('_$_.set_class', id, key, hash_arg, b.literal(is_html))),
|
|
1046
1089
|
expression,
|
|
1090
|
+
identity: class_attribute.value,
|
|
1047
1091
|
initial: b.literal(''),
|
|
1048
1092
|
});
|
|
1049
1093
|
} else {
|
|
@@ -1063,14 +1107,16 @@ const visitors = {
|
|
|
1063
1107
|
const id = state.flush_node();
|
|
1064
1108
|
const metadata = { tracking: false, await: false };
|
|
1065
1109
|
const expression = visit(style_attribute.value, { ...state, metadata });
|
|
1066
|
-
const name = style_attribute.name.name;
|
|
1067
|
-
|
|
1068
|
-
const statement = b.stmt(b.call('_$_.set_attribute', id, b.literal(name), expression));
|
|
1069
1110
|
|
|
1070
1111
|
if (metadata.tracking) {
|
|
1071
|
-
local_updates.push({
|
|
1112
|
+
local_updates.push({
|
|
1113
|
+
operation: (key) => b.stmt(b.call('_$_.set_style', id, key)),
|
|
1114
|
+
identity: style_attribute.value,
|
|
1115
|
+
expression,
|
|
1116
|
+
initial: b.void0,
|
|
1117
|
+
});
|
|
1072
1118
|
} else {
|
|
1073
|
-
state.init.push(
|
|
1119
|
+
state.init.push(b.stmt(b.call('_$_.set_style', id, expression)));
|
|
1074
1120
|
}
|
|
1075
1121
|
}
|
|
1076
1122
|
}
|
|
@@ -1084,7 +1130,9 @@ const visitors = {
|
|
|
1084
1130
|
);
|
|
1085
1131
|
}
|
|
1086
1132
|
|
|
1133
|
+
/** @type {Array<Statement>} */
|
|
1087
1134
|
const init = [];
|
|
1135
|
+
/** @type {Array<Statement>} */
|
|
1088
1136
|
const update = [];
|
|
1089
1137
|
|
|
1090
1138
|
if (!is_void) {
|
|
@@ -1100,7 +1148,7 @@ const visitors = {
|
|
|
1100
1148
|
|
|
1101
1149
|
if (update.length > 0) {
|
|
1102
1150
|
if (state.scope.parent.declarations.size > 0) {
|
|
1103
|
-
apply_updates(init, update);
|
|
1151
|
+
apply_updates(init, update, state);
|
|
1104
1152
|
} else {
|
|
1105
1153
|
state.update.push(...update);
|
|
1106
1154
|
}
|
|
@@ -2252,6 +2300,7 @@ function transform_children(children, context) {
|
|
|
2252
2300
|
state.update.push({
|
|
2253
2301
|
operation: (key) => b.stmt(b.call('_$_.set_text', id, key)),
|
|
2254
2302
|
expression,
|
|
2303
|
+
identity: node.expression,
|
|
2255
2304
|
initial: b.literal(' '),
|
|
2256
2305
|
});
|
|
2257
2306
|
if (metadata.await) {
|
|
@@ -2279,6 +2328,7 @@ function transform_children(children, context) {
|
|
|
2279
2328
|
state.update.push({
|
|
2280
2329
|
operation: (key) => b.stmt(b.call('_$_.set_text', id, key)),
|
|
2281
2330
|
expression,
|
|
2331
|
+
identity: node.expression,
|
|
2282
2332
|
initial: b.literal(' '),
|
|
2283
2333
|
});
|
|
2284
2334
|
if (metadata.await) {
|
|
@@ -2356,7 +2406,7 @@ function transform_body(body, { visit, state }) {
|
|
|
2356
2406
|
// In TypeScript mode, just add the update statements directly
|
|
2357
2407
|
body_state.init.push(...body_state.update);
|
|
2358
2408
|
} else {
|
|
2359
|
-
apply_updates(body_state.init, body_state.update);
|
|
2409
|
+
apply_updates(body_state.init, body_state.update, state);
|
|
2360
2410
|
}
|
|
2361
2411
|
}
|
|
2362
2412
|
|
|
@@ -24,6 +24,65 @@ function is_in_global_block(path) {
|
|
|
24
24
|
return path.some((node) => node.type === 'Rule' && node.metadata.is_global_block);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Check if we're inside a pseudo-class selector that's INSIDE a :global() wrapper
|
|
29
|
+
* or adjacent to a :global modifier
|
|
30
|
+
* @param {any[]} path
|
|
31
|
+
*/
|
|
32
|
+
function is_in_global_pseudo(path) {
|
|
33
|
+
// Walk up the path to find if we're inside a :global() pseudo-class selector with args
|
|
34
|
+
// or if we're in a pseudo-class that's in the same RelativeSelector as a :global modifier
|
|
35
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
36
|
+
const node = path[i];
|
|
37
|
+
|
|
38
|
+
// Case 1: :global(...) with args - we're inside it
|
|
39
|
+
if (node.type === 'PseudoClassSelector' && node.name === 'global' && node.args !== null) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Case 2: We're in a PseudoClassSelector (like :is, :where, :has, :not)
|
|
44
|
+
// Check if there's a :global modifier in the same RelativeSelector
|
|
45
|
+
if (
|
|
46
|
+
node.type === 'PseudoClassSelector' &&
|
|
47
|
+
(node.name === 'is' || node.name === 'where' || node.name === 'has' || node.name === 'not')
|
|
48
|
+
) {
|
|
49
|
+
// Look for the parent RelativeSelector
|
|
50
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
51
|
+
const ancestor = path[j];
|
|
52
|
+
if (ancestor.type === 'RelativeSelector') {
|
|
53
|
+
// Check if this RelativeSelector has a :global modifier (no args)
|
|
54
|
+
const hasGlobalModifier = ancestor.selectors.some(
|
|
55
|
+
(s) => s.type === 'PseudoClassSelector' && s.name === 'global' && s.args === null,
|
|
56
|
+
);
|
|
57
|
+
if (hasGlobalModifier) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if a rule has :global in the middle (like `div :global p`)
|
|
70
|
+
* These rules should treat nested selectors as global
|
|
71
|
+
* @param {AST.CSS.Rule} rule
|
|
72
|
+
*/
|
|
73
|
+
function has_global_in_middle(rule) {
|
|
74
|
+
for (const complex_selector of rule.prelude.children) {
|
|
75
|
+
for (let i = 0; i < complex_selector.children.length; i++) {
|
|
76
|
+
const child = complex_selector.children[i];
|
|
77
|
+
// Check if this is a :global selector that's not at the start
|
|
78
|
+
if (i > 0 && child.metadata.is_global) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
27
86
|
function remove_global_pseudo_class(selector, combinator, state) {
|
|
28
87
|
if (selector.args === null) {
|
|
29
88
|
let start = selector.start;
|
|
@@ -75,13 +134,19 @@ function is_empty(rule, is_in_global_block) {
|
|
|
75
134
|
return rule.block.children.length === 0;
|
|
76
135
|
}
|
|
77
136
|
|
|
137
|
+
// Rules with :global in the middle (like `div :global p`) should treat nested rules as global
|
|
138
|
+
const has_mid_global = has_global_in_middle(rule);
|
|
139
|
+
|
|
78
140
|
for (const child of rule.block.children) {
|
|
79
141
|
if (child.type === 'Declaration') {
|
|
80
142
|
return false;
|
|
81
143
|
}
|
|
82
144
|
|
|
83
145
|
if (child.type === 'Rule') {
|
|
84
|
-
if (
|
|
146
|
+
if (
|
|
147
|
+
(is_used(child) || is_in_global_block || has_mid_global) &&
|
|
148
|
+
!is_empty(child, is_in_global_block || has_mid_global)
|
|
149
|
+
) {
|
|
85
150
|
return false;
|
|
86
151
|
}
|
|
87
152
|
}
|
|
@@ -196,8 +261,10 @@ const visitors = {
|
|
|
196
261
|
},
|
|
197
262
|
SelectorList(node, { state, next, path }) {
|
|
198
263
|
// Only add comments if we're not inside a complex selector that itself is unused or a global block
|
|
264
|
+
// or inside a pseudo-class that's part of a global selector
|
|
199
265
|
if (
|
|
200
266
|
!is_in_global_block(path) &&
|
|
267
|
+
!is_in_global_pseudo(path) &&
|
|
201
268
|
!path.find((n) => n.type === 'ComplexSelector' && !n.metadata.used)
|
|
202
269
|
) {
|
|
203
270
|
const children = node.children;
|
|
@@ -280,8 +347,39 @@ const visitors = {
|
|
|
280
347
|
ComplexSelector(node, context) {
|
|
281
348
|
const before_bumped = context.state.specificity.bumped;
|
|
282
349
|
|
|
350
|
+
// Check if we're inside a :has/:is/:where/:not pseudo-class that's part of a global selector
|
|
351
|
+
// In that case, we should still scope the contents even though the parent is global
|
|
352
|
+
const parentPath = context.path;
|
|
353
|
+
let insideScopingPseudo = false;
|
|
354
|
+
|
|
355
|
+
// Walk up the path to find if we're inside args of :has/:is/:where/:not
|
|
356
|
+
for (let i = parentPath.length - 1; i >= 0; i--) {
|
|
357
|
+
const pathNode = parentPath[i];
|
|
358
|
+
|
|
359
|
+
// Check if we're inside a SelectorList that belongs to a scoping pseudo-class
|
|
360
|
+
if (pathNode.type === 'SelectorList' && i > 0) {
|
|
361
|
+
const parent = parentPath[i - 1];
|
|
362
|
+
if (
|
|
363
|
+
parent.type === 'PseudoClassSelector' &&
|
|
364
|
+
(parent.name === 'has' ||
|
|
365
|
+
parent.name === 'is' ||
|
|
366
|
+
parent.name === 'where' ||
|
|
367
|
+
parent.name === 'not')
|
|
368
|
+
) {
|
|
369
|
+
// Now check if this pseudo-class is part of a global RelativeSelector
|
|
370
|
+
for (let j = i - 2; j >= 0; j--) {
|
|
371
|
+
if (parentPath[j].type === 'RelativeSelector' && parentPath[j].metadata?.is_global) {
|
|
372
|
+
insideScopingPseudo = true;
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
283
381
|
for (const relative_selector of node.children) {
|
|
284
|
-
if (relative_selector.metadata.is_global) {
|
|
382
|
+
if (relative_selector.metadata.is_global && !insideScopingPseudo) {
|
|
285
383
|
const global = /** @type {AST.CSS.PseudoClassSelector} */ (relative_selector.selectors[0]);
|
|
286
384
|
remove_global_pseudo_class(global, relative_selector.combinator, context.state);
|
|
287
385
|
|
|
@@ -303,7 +401,8 @@ const visitors = {
|
|
|
303
401
|
}
|
|
304
402
|
}
|
|
305
403
|
|
|
306
|
-
if
|
|
404
|
+
// Skip scoping if we're inside a global block
|
|
405
|
+
if (relative_selector.metadata.scoped && !is_in_global_block(context.path)) {
|
|
307
406
|
if (relative_selector.selectors.length === 1) {
|
|
308
407
|
// skip standalone :is/:where/& selectors
|
|
309
408
|
const selector = relative_selector.selectors[0];
|
|
@@ -28,12 +28,6 @@ export function init_operations() {
|
|
|
28
28
|
// @ts-expect-error
|
|
29
29
|
element_prototype.__click = undefined;
|
|
30
30
|
// @ts-expect-error
|
|
31
|
-
element_prototype.__attributes = null;
|
|
32
|
-
// @ts-expect-error
|
|
33
|
-
element_prototype.__styles = null;
|
|
34
|
-
// @ts-expect-error
|
|
35
|
-
element_prototype.__e = undefined;
|
|
36
|
-
// @ts-expect-error
|
|
37
31
|
event_target_prototype.__root = undefined;
|
|
38
32
|
}
|
|
39
33
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** @import { Block } from '#client' */
|
|
2
2
|
|
|
3
3
|
import { destroy_block, ref } from './blocks.js';
|
|
4
|
-
import { REF_PROP
|
|
4
|
+
import { REF_PROP } from './constants.js';
|
|
5
5
|
import {
|
|
6
6
|
get_descriptors,
|
|
7
7
|
get_own_property_symbols,
|
|
@@ -69,25 +69,29 @@ function get_setters(element) {
|
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* @param {Element} element
|
|
72
|
-
* @param {string} attribute
|
|
73
72
|
* @param {any} value
|
|
74
73
|
* @returns {void}
|
|
75
74
|
*/
|
|
76
|
-
export function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
element.__styles = {};
|
|
75
|
+
export function set_style(element, value) {
|
|
76
|
+
if (value == null) {
|
|
77
|
+
element.removeAttribute('style');
|
|
78
|
+
} else if (typeof value !== 'string') {
|
|
79
|
+
apply_styles(/** @type {HTMLElement} */ (element), value);
|
|
80
|
+
} else {
|
|
81
|
+
// @ts-ignore
|
|
82
|
+
element.style.cssText = value;
|
|
85
83
|
}
|
|
84
|
+
}
|
|
86
85
|
|
|
86
|
+
/**
|
|
87
|
+
* @param {Element} element
|
|
88
|
+
* @param {string} attribute
|
|
89
|
+
* @param {any} value
|
|
90
|
+
* @returns {void}
|
|
91
|
+
*/
|
|
92
|
+
export function set_attribute(element, attribute, value) {
|
|
87
93
|
if (value == null) {
|
|
88
94
|
element.removeAttribute(attribute);
|
|
89
|
-
} else if (attribute === 'style' && typeof value !== 'string') {
|
|
90
|
-
apply_styles(/** @type {HTMLElement} */ (element), value);
|
|
91
95
|
} else if (typeof value !== 'string' && get_setters(element).includes(attribute)) {
|
|
92
96
|
/** @type {any} */ (element)[attribute] = value;
|
|
93
97
|
} else {
|
|
@@ -97,13 +101,13 @@ export function set_attribute(element, attribute, value) {
|
|
|
97
101
|
|
|
98
102
|
/**
|
|
99
103
|
* @param {HTMLElement} element
|
|
100
|
-
* @param {HTMLElement['style']}
|
|
104
|
+
* @param {HTMLElement['style']} new_styles
|
|
101
105
|
*/
|
|
102
|
-
export function apply_styles(element,
|
|
106
|
+
export function apply_styles(element, new_styles) {
|
|
103
107
|
const style = element.style;
|
|
104
108
|
const new_properties = new Set();
|
|
105
109
|
|
|
106
|
-
for (const [property, value] of Object.entries(
|
|
110
|
+
for (const [property, value] of Object.entries(new_styles)) {
|
|
107
111
|
const normalized_property = normalize_css_property_name(property);
|
|
108
112
|
const normalized_value = String(value);
|
|
109
113
|
|
|
@@ -132,6 +136,8 @@ function set_attribute_helper(element, key, value) {
|
|
|
132
136
|
if (key === 'class') {
|
|
133
137
|
const is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
|
|
134
138
|
set_class(/** @type {HTMLElement} */ (element), value, undefined, is_html);
|
|
139
|
+
} else if (key === 'style') {
|
|
140
|
+
set_style(/** @type {HTMLElement} */ (element), value);
|
|
135
141
|
} else if (key === '#class') {
|
|
136
142
|
// Special case for static class when spreading props
|
|
137
143
|
element.classList.add(value);
|