@swc/plugin-styled-jsx 1.5.70 → 1.5.72
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/Cargo.toml +3 -8
- package/package.json +1 -1
- package/swc_plugin_styled_jsx.wasm +0 -0
- package/transform/Cargo.toml +10 -9
- package/transform/src/lib.rs +1 -0
- package/transform/src/lifetime.rs +408 -0
- package/transform/src/style.rs +3 -0
- package/transform/src/transform_css.rs +390 -260
- package/transform/src/visitor.rs +14 -1
- package/transform/tests/errors/ts-with-css-resolve/output.js +1 -1
- package/transform/tests/fixture/css-selector-after-pseudo/output.js +1 -1
- package/transform/tests/fixture/fragment/output.js +1 -1
- package/transform/tests/fixture/global/output.js +1 -1
- package/transform/tests/fixture/global-redundant/output.js +1 -1
- package/transform/tests/fixture/issue-30480/input.js +5 -1
- package/transform/tests/fixture/issue-30480/output.js +3 -3
- package/transform/tests/fixture/issue-30570/output.js +1 -1
- package/transform/tests/fixture/styles/output.js +4 -4
- package/transform/tests/fixture/tpl-placeholder-1-as-property/output.js +1 -2
- package/transform/tests/fixture/tpl-placeholder-4-as-part-of-value-in-multiple/output.js +1 -1
- package/transform/tests/fixture/transform-css/input.js +22 -22
- package/transform/tests/fixture/transform-css/output.js +3 -3
- package/transform/tests/fixture/transform-css-complex-selector/output.js +1 -1
- package/transform/tests/fixture/transform-css-complex-selector-2/input.js +12 -0
- package/transform/tests/fixture/transform-css-complex-selector-2/output.js +8 -0
- package/transform/tests/fixture/transform-css-global/output.js +1 -1
- package/transform/tests/fixture/transform-css-media-query/output.js +1 -1
- package/transform/tests/fixture/transform-css-normal/output.js +1 -1
- package/transform/tests/fixture/transform-css-nth-1/output.js +1 -1
- /package/transform/tests/fixture/{expressions → .expressions}/input.js +0 -0
- /package/transform/tests/fixture/{expressions → .expressions}/output.js +0 -0
|
@@ -1,40 +1,39 @@
|
|
|
1
|
-
use std::{
|
|
1
|
+
use std::{
|
|
2
|
+
borrow::Cow,
|
|
3
|
+
convert::Infallible,
|
|
4
|
+
fmt::Debug,
|
|
5
|
+
mem::transmute,
|
|
6
|
+
panic::{catch_unwind, AssertUnwindSafe},
|
|
7
|
+
sync::Arc,
|
|
8
|
+
};
|
|
2
9
|
|
|
3
|
-
use easy_error::{bail, Error};
|
|
10
|
+
use easy_error::{bail, Error, ResultExt};
|
|
11
|
+
use lightningcss::{
|
|
12
|
+
properties::custom::{TokenList, TokenOrValue},
|
|
13
|
+
selector::{Combinator, Component, PseudoClass, Selector},
|
|
14
|
+
stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet},
|
|
15
|
+
traits::{ParseWithOptions, ToCss},
|
|
16
|
+
values::ident::Ident,
|
|
17
|
+
visit_types,
|
|
18
|
+
visitor::{Visit, VisitTypes, Visitor},
|
|
19
|
+
};
|
|
20
|
+
use parcel_selectors::{parser::SelectorIter, SelectorImpl};
|
|
4
21
|
use swc_core::{
|
|
5
|
-
common::{
|
|
6
|
-
|
|
7
|
-
SyntaxContext, DUMMY_SP,
|
|
8
|
-
},
|
|
9
|
-
css::{
|
|
10
|
-
ast::*,
|
|
11
|
-
codegen::{
|
|
12
|
-
writer::basic::{BasicCssWriter, BasicCssWriterConfig},
|
|
13
|
-
CodeGenerator, CodegenConfig, Emit,
|
|
14
|
-
},
|
|
15
|
-
parser::{
|
|
16
|
-
lexer::Lexer,
|
|
17
|
-
parse_input,
|
|
18
|
-
parser::{
|
|
19
|
-
input::{InputType, Tokens},
|
|
20
|
-
Parser, ParserConfig,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
prefixer::prefixer,
|
|
24
|
-
visit::{VisitMut, VisitMutWith},
|
|
25
|
-
},
|
|
26
|
-
ecma::{
|
|
27
|
-
ast::{Expr, Tpl, TplElement},
|
|
28
|
-
parser::StringInput,
|
|
29
|
-
},
|
|
22
|
+
common::{errors::HANDLER, SourceMap, DUMMY_SP},
|
|
23
|
+
ecma::ast::{Expr, Tpl, TplElement},
|
|
30
24
|
};
|
|
31
|
-
use tracing::{debug, trace};
|
|
25
|
+
use tracing::{debug, error, trace};
|
|
32
26
|
|
|
33
27
|
use crate::{
|
|
28
|
+
lifetime::owned_selector,
|
|
34
29
|
style::LocalStyle,
|
|
35
30
|
utils::{hash_string, string_literal_expr},
|
|
36
31
|
};
|
|
37
32
|
|
|
33
|
+
#[cfg_attr(
|
|
34
|
+
debug_assertions,
|
|
35
|
+
tracing::instrument(skip(_cm, style_info, class_name))
|
|
36
|
+
)]
|
|
38
37
|
pub fn transform_css(
|
|
39
38
|
_cm: Arc<SourceMap>,
|
|
40
39
|
style_info: &LocalStyle,
|
|
@@ -42,30 +41,24 @@ pub fn transform_css(
|
|
|
42
41
|
class_name: &Option<String>,
|
|
43
42
|
) -> Result<Expr, Error> {
|
|
44
43
|
debug!("CSS: \n{}", style_info.css);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
let
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
style_info.css_span.lo,
|
|
56
|
-
style_info.css_span.hi,
|
|
57
|
-
),
|
|
58
|
-
config,
|
|
44
|
+
let css_str = strip_comments(&style_info.css);
|
|
45
|
+
|
|
46
|
+
let result: Result<StyleSheet, _> = StyleSheet::parse(
|
|
47
|
+
&css_str,
|
|
48
|
+
ParserOptions {
|
|
49
|
+
// We cannot use css_modules for `:global` because lightningcss does not support
|
|
50
|
+
// parsing-only mode.
|
|
51
|
+
css_modules: None,
|
|
52
|
+
..Default::default()
|
|
53
|
+
},
|
|
59
54
|
);
|
|
60
|
-
let mut parser = Parser::new(lexer, config);
|
|
61
|
-
|
|
62
|
-
let result: Result<Stylesheet, _> = parser.parse_all();
|
|
63
55
|
let mut ss = match result {
|
|
64
56
|
Ok(ss) => ss,
|
|
65
|
-
Err(
|
|
57
|
+
Err(_err) => {
|
|
66
58
|
HANDLER.with(|handler| {
|
|
67
59
|
// Print css parsing errors
|
|
68
|
-
|
|
60
|
+
// TODO:
|
|
61
|
+
// err.to_diagnostics(handler).emit();
|
|
69
62
|
|
|
70
63
|
// TODO(kdy1): We may print css so the user can see the error, and report it.
|
|
71
64
|
|
|
@@ -80,36 +73,45 @@ pub fn transform_css(
|
|
|
80
73
|
bail!("Failed to parse css");
|
|
81
74
|
}
|
|
82
75
|
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
|
|
77
|
+
// Apply auto prefixer
|
|
78
|
+
// TODO:
|
|
79
|
+
ss.minify(MinifyOptions {
|
|
80
|
+
..Default::default()
|
|
81
|
+
})
|
|
82
|
+
.expect("failed to minify/auto-prefix css");
|
|
83
|
+
ss.visit(&mut CssNamespace {
|
|
86
84
|
class_name: match class_name {
|
|
87
85
|
Some(s) => s.clone(),
|
|
88
86
|
None => format!("jsx-{}", &hash_string(&style_info.hash)),
|
|
89
87
|
},
|
|
90
88
|
is_global,
|
|
91
89
|
is_dynamic: style_info.is_dynamic,
|
|
92
|
-
})
|
|
90
|
+
})
|
|
91
|
+
.expect("failed to transform css");
|
|
93
92
|
|
|
94
|
-
let
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
let res = ss
|
|
94
|
+
.to_css(PrinterOptions {
|
|
95
|
+
minify: true,
|
|
96
|
+
// TODO
|
|
97
|
+
// targets: (),
|
|
98
|
+
..Default::default()
|
|
99
|
+
})
|
|
100
|
+
.context("failed to print css")?;
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
}
|
|
102
|
+
debug!("Transformed CSS: \n{}", res.code);
|
|
101
103
|
|
|
102
104
|
if style_info.expressions.is_empty() {
|
|
103
|
-
return Ok(string_literal_expr(&
|
|
105
|
+
return Ok(string_literal_expr(&res.code));
|
|
104
106
|
}
|
|
105
107
|
|
|
106
|
-
let mut parts: Vec<&str> =
|
|
108
|
+
let mut parts: Vec<&str> = res.code.split("__styled-jsx-placeholder-").collect();
|
|
107
109
|
let mut final_expressions = vec![];
|
|
108
110
|
for i in parts.iter_mut().skip(1) {
|
|
109
|
-
let (num_len, expression_index) = read_number(i);
|
|
111
|
+
let (num_len, expression_index) = read_number(i, &style_info.is_expr_property);
|
|
110
112
|
final_expressions.push(style_info.expressions[expression_index].clone());
|
|
111
|
-
let
|
|
112
|
-
*i =
|
|
113
|
+
let substring = &i[(num_len + 2)..];
|
|
114
|
+
*i = substring;
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
Ok(Expr::Tpl(Tpl {
|
|
@@ -129,266 +131,394 @@ pub fn transform_css(
|
|
|
129
131
|
}))
|
|
130
132
|
}
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
fn strip_comments(s: &str) -> Cow<str> {
|
|
135
|
+
if !s.contains("//") {
|
|
136
|
+
return Cow::Borrowed(s);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let mut buf = String::with_capacity(s.len());
|
|
140
|
+
|
|
141
|
+
for line in s.lines() {
|
|
142
|
+
let line = line.trim();
|
|
143
|
+
|
|
144
|
+
if let Some(index) = line.find("//") {
|
|
145
|
+
buf.push_str(&line[..index]);
|
|
146
|
+
} else {
|
|
147
|
+
buf.push_str(line);
|
|
148
|
+
}
|
|
149
|
+
buf.push('\n');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
Cow::Owned(buf)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// Returns `(length, expression_index)`
|
|
156
|
+
fn read_number(s: &str, is_expr_property: &[bool]) -> (usize, usize) {
|
|
134
157
|
for (idx, c) in s.char_indices() {
|
|
135
158
|
if c.is_ascii_digit() {
|
|
136
159
|
continue;
|
|
137
160
|
}
|
|
138
161
|
|
|
139
162
|
// For 10, we reach here after `0`.
|
|
140
|
-
let
|
|
163
|
+
let expression_index = s[0..idx].parse().expect("failed to parse");
|
|
164
|
+
|
|
165
|
+
if is_expr_property[expression_index] {
|
|
166
|
+
return (idx + 3, expression_index);
|
|
167
|
+
}
|
|
141
168
|
|
|
142
|
-
return (idx,
|
|
169
|
+
return (idx, expression_index);
|
|
143
170
|
}
|
|
144
171
|
|
|
145
172
|
unreachable!("read_number(`{}`) is invalid because it is empty", s)
|
|
146
173
|
}
|
|
147
174
|
|
|
148
|
-
struct
|
|
175
|
+
struct CssNamespace {
|
|
149
176
|
class_name: String,
|
|
150
177
|
is_global: bool,
|
|
151
178
|
is_dynamic: bool,
|
|
152
179
|
}
|
|
153
180
|
|
|
154
|
-
impl
|
|
155
|
-
|
|
156
|
-
#[cfg(debug_assertions)]
|
|
157
|
-
let _tracing = {
|
|
158
|
-
// This will add information to the log messages, only for debug build.
|
|
159
|
-
// Note that we use cargo feature to remove all logging on production builds.
|
|
181
|
+
impl<'i> Visitor<'i> for CssNamespace {
|
|
182
|
+
type Error = Infallible;
|
|
160
183
|
|
|
161
|
-
|
|
162
|
-
{
|
|
163
|
-
let mut wr = BasicCssWriter::new(&mut code, None, BasicCssWriterConfig::default());
|
|
164
|
-
let mut gen = CodeGenerator::new(&mut wr, CodegenConfig { minify: true });
|
|
184
|
+
const TYPES: VisitTypes = visit_types!(SELECTORS);
|
|
165
185
|
|
|
166
|
-
|
|
167
|
-
|
|
186
|
+
fn visit_selector(&mut self, selector: &mut Selector<'i>) -> Result<(), Self::Error> {
|
|
187
|
+
let mut new_selectors = vec![];
|
|
188
|
+
let mut combinator = None;
|
|
168
189
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
190
|
+
#[cfg(debug_assertions)]
|
|
191
|
+
let _tracing = tracing::span!(
|
|
192
|
+
tracing::Level::ERROR,
|
|
193
|
+
"visit_selector",
|
|
194
|
+
len = selector.len()
|
|
195
|
+
)
|
|
196
|
+
.entered();
|
|
197
|
+
|
|
198
|
+
let mut iter = selector.iter();
|
|
199
|
+
loop {
|
|
200
|
+
#[cfg(debug_assertions)]
|
|
201
|
+
let _tracing = tracing::span!(
|
|
202
|
+
tracing::Level::ERROR,
|
|
203
|
+
"visit_selector/loop",
|
|
204
|
+
len = iter.selector_length()
|
|
176
205
|
)
|
|
177
|
-
.entered()
|
|
178
|
-
};
|
|
206
|
+
.entered();
|
|
179
207
|
|
|
180
|
-
|
|
181
|
-
|
|
208
|
+
if combinator.is_none() {
|
|
209
|
+
combinator = iter.next_sequence();
|
|
210
|
+
}
|
|
182
211
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
.struct_span_err(
|
|
192
|
-
selector.span,
|
|
193
|
-
"Failed to transform one off global selector",
|
|
194
|
-
)
|
|
195
|
-
.emit()
|
|
196
|
-
});
|
|
197
|
-
new_selectors.push(sel);
|
|
198
|
-
}
|
|
212
|
+
match self.get_transformed_selectors(combinator, &mut iter) {
|
|
213
|
+
Ok(transformed_selectors) => {
|
|
214
|
+
if transformed_selectors.is_empty() {
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if cfg!(debug_assertions) {
|
|
219
|
+
debug!("Transformed as: {:?}", SafeDebug(&transformed_selectors))
|
|
199
220
|
}
|
|
200
221
|
|
|
201
|
-
|
|
222
|
+
new_selectors.push(transformed_selectors);
|
|
202
223
|
}
|
|
203
|
-
|
|
204
|
-
|
|
224
|
+
Err(_) => {
|
|
225
|
+
error!("Failed to transform one off global selector");
|
|
226
|
+
// TODO:
|
|
227
|
+
|
|
228
|
+
// HANDLER.with(|handler| {
|
|
229
|
+
// handler
|
|
230
|
+
// .struct_span_err(
|
|
231
|
+
// selector.span,
|
|
232
|
+
// "Failed to transform one off global selector",
|
|
233
|
+
// )
|
|
234
|
+
// .emit()
|
|
235
|
+
// });
|
|
236
|
+
|
|
237
|
+
new_selectors.push(iter.clone().cloned().collect());
|
|
205
238
|
}
|
|
206
|
-
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
trace!(
|
|
242
|
+
"Selector length after transform: {}",
|
|
243
|
+
iter.selector_length()
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
if combinator.is_none() {
|
|
247
|
+
combinator = iter.next_sequence();
|
|
248
|
+
if combinator.is_none() {
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
combinator = None;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let new: Vec<_> = RemoveWhitespace {
|
|
257
|
+
iter: new_selectors.into_iter().rev().flatten(),
|
|
258
|
+
prev: None,
|
|
207
259
|
}
|
|
260
|
+
.collect();
|
|
261
|
+
debug!("Selector vector: {:?}", SafeDebug(&new));
|
|
262
|
+
|
|
263
|
+
*selector = Selector::from(new);
|
|
264
|
+
|
|
265
|
+
Ok(())
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
struct RemoveWhitespace<'i, I> {
|
|
270
|
+
iter: I,
|
|
271
|
+
prev: Option<Component<'i>>,
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
impl<'i, I> Iterator for RemoveWhitespace<'i, I>
|
|
275
|
+
where
|
|
276
|
+
I: Iterator<Item = Component<'i>>,
|
|
277
|
+
{
|
|
278
|
+
type Item = Component<'i>;
|
|
208
279
|
|
|
209
|
-
|
|
280
|
+
fn next(&mut self) -> Option<Self::Item> {
|
|
281
|
+
match self.prev.take() {
|
|
282
|
+
Some(Component::Combinator(Combinator::Descendant)) => {
|
|
283
|
+
self.prev = self.iter.next();
|
|
284
|
+
|
|
285
|
+
match self.prev {
|
|
286
|
+
Some(Component::Combinator(..)) => self.next(),
|
|
287
|
+
_ => Some(Component::Combinator(Combinator::Descendant)),
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
Some(v) => Some(v),
|
|
291
|
+
_ => {
|
|
292
|
+
self.prev = self.iter.next();
|
|
293
|
+
|
|
294
|
+
if self.prev.is_some() {
|
|
295
|
+
self.next()
|
|
296
|
+
} else {
|
|
297
|
+
None
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
210
301
|
}
|
|
211
302
|
}
|
|
212
303
|
|
|
213
|
-
impl
|
|
214
|
-
|
|
304
|
+
impl CssNamespace {
|
|
305
|
+
#[cfg_attr(debug_assertions, tracing::instrument(skip(self, node)))]
|
|
306
|
+
fn get_transformed_selectors<'a, 'i, Impl>(
|
|
215
307
|
&mut self,
|
|
216
308
|
combinator: Option<Combinator>,
|
|
217
|
-
|
|
218
|
-
) -> Result<Vec<
|
|
309
|
+
node: &mut SelectorIter<'a, 'i, Impl>,
|
|
310
|
+
) -> Result<Vec<Component<'i>>, Error>
|
|
311
|
+
where
|
|
312
|
+
Impl: SelectorImpl<'i>,
|
|
313
|
+
SelectorIter<'a, 'i, Impl>: Iterator<Item = &'a Component<'i>>,
|
|
314
|
+
{
|
|
315
|
+
let mut result: Vec<Component<'i>> = vec![];
|
|
316
|
+
|
|
219
317
|
let mut pseudo_index = None;
|
|
220
318
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
319
|
+
let node: Vec<Component<'i>> = node.fuse().cloned().collect::<Vec<_>>();
|
|
320
|
+
|
|
321
|
+
if node.is_empty() {
|
|
322
|
+
return Ok(result);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
#[cfg(debug_assertions)]
|
|
326
|
+
{
|
|
327
|
+
let prev_sel = Selector::from(node.clone());
|
|
328
|
+
debug!("Input selector: {:?}", SafeDebug(&prev_sel))
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
for (i, component) in node.iter().enumerate() {
|
|
332
|
+
trace!("Selector at {}", i);
|
|
333
|
+
|
|
334
|
+
#[cfg(debug_assertions)]
|
|
335
|
+
{
|
|
336
|
+
debug!("Component: {:?}", SafeDebug(&component))
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Look for :global
|
|
340
|
+
let children: Selector<'i> = match &component {
|
|
341
|
+
Component::NonTSPseudoClass(PseudoClass::CustomFunction { name, arguments }) => {
|
|
342
|
+
if &**name != "global" {
|
|
343
|
+
if pseudo_index.is_none() {
|
|
344
|
+
pseudo_index = Some(i);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
parse_token_list(arguments)
|
|
351
|
+
}
|
|
352
|
+
Component::PseudoElement(_)
|
|
353
|
+
| Component::NonTSPseudoClass(..)
|
|
354
|
+
| Component::Negation(..)
|
|
355
|
+
| Component::Root
|
|
356
|
+
| Component::Empty
|
|
357
|
+
| Component::Scope
|
|
358
|
+
| Component::Nth(..)
|
|
359
|
+
| Component::NthOf(..)
|
|
360
|
+
| Component::Slotted(..)
|
|
361
|
+
| Component::Part(..)
|
|
362
|
+
| Component::Host(..)
|
|
363
|
+
| Component::Where(..)
|
|
364
|
+
| Component::Is(..)
|
|
365
|
+
| Component::Any(..)
|
|
366
|
+
| Component::Has(..) => {
|
|
229
367
|
if pseudo_index.is_none() {
|
|
230
368
|
pseudo_index = Some(i);
|
|
231
369
|
}
|
|
232
370
|
|
|
233
371
|
continue;
|
|
234
372
|
}
|
|
235
|
-
_ =>
|
|
373
|
+
_ => {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
236
376
|
};
|
|
237
377
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
let mut tokens = {
|
|
251
|
-
let lo = tokens.first().map(|v| v.span_lo()).unwrap_or(BytePos(0));
|
|
252
|
-
let hi = tokens.last().map(|v| v.span_hi()).unwrap_or(BytePos(0));
|
|
253
|
-
|
|
254
|
-
Tokens {
|
|
255
|
-
span: Span::new(lo, hi, Default::default()),
|
|
256
|
-
tokens,
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
// Because it is allowed to write `.bar :global(> .foo) {}` or .bar
|
|
261
|
-
// :global(.foo) {}`, so selector can be complex or relative (it violates the
|
|
262
|
-
// specification), but it is popular usage, so we just add `a >` at top and then
|
|
263
|
-
// remove it
|
|
264
|
-
let mut front_tokens = get_front_selector_tokens(&tokens);
|
|
265
|
-
|
|
266
|
-
front_tokens.extend(tokens.tokens);
|
|
267
|
-
|
|
268
|
-
tokens.tokens = front_tokens;
|
|
269
|
-
|
|
270
|
-
let complex_selectors = panic::catch_unwind(|| {
|
|
271
|
-
let x: ComplexSelector = parse_input(
|
|
272
|
-
InputType::Tokens(&tokens),
|
|
273
|
-
ParserConfig {
|
|
274
|
-
allow_wrong_line_comments: true,
|
|
275
|
-
css_modules: true,
|
|
276
|
-
..Default::default()
|
|
277
|
-
},
|
|
278
|
-
// TODO(kdy1): We might be able to report syntax errors.
|
|
279
|
-
&mut vec![],
|
|
280
|
-
)
|
|
281
|
-
.unwrap();
|
|
282
|
-
x
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
return match complex_selectors {
|
|
286
|
-
Ok(complex_selectors) => {
|
|
287
|
-
let mut v = complex_selectors.children[1..].to_vec();
|
|
288
|
-
|
|
289
|
-
if let ComplexSelectorChildren::Combinator(Combinator {
|
|
290
|
-
value: CombinatorValue::Descendant,
|
|
291
|
-
..
|
|
292
|
-
}) = v[0]
|
|
293
|
-
{
|
|
294
|
-
v.remove(0);
|
|
295
|
-
}
|
|
378
|
+
let mut complex_selectors =
|
|
379
|
+
children.iter_raw_match_order().cloned().collect::<Vec<_>>();
|
|
380
|
+
// Remove `a`
|
|
381
|
+
complex_selectors.pop();
|
|
382
|
+
if let Some(Component::Combinator(Combinator::Descendant)) = complex_selectors.last() {
|
|
383
|
+
complex_selectors.pop();
|
|
384
|
+
} else if let Some(Component::Combinator(c)) = complex_selectors.last() {
|
|
385
|
+
let c = *c;
|
|
386
|
+
complex_selectors.pop();
|
|
387
|
+
|
|
388
|
+
complex_selectors.insert(0, Component::Combinator(c))
|
|
389
|
+
}
|
|
296
390
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
391
|
+
if let Component::Combinator(Combinator::Descendant) = complex_selectors[0] {
|
|
392
|
+
complex_selectors.remove(0);
|
|
393
|
+
}
|
|
300
394
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
let mut result = vec![];
|
|
305
|
-
|
|
306
|
-
if let Some(combinator) = combinator {
|
|
307
|
-
match v.get(0) {
|
|
308
|
-
// `Descendant` combinator can't be the first because we removed it
|
|
309
|
-
// above
|
|
310
|
-
Some(ComplexSelectorChildren::Combinator(..))
|
|
311
|
-
if combinator.value == CombinatorValue::Descendant => {}
|
|
312
|
-
_ => {
|
|
313
|
-
result.push(ComplexSelectorChildren::Combinator(combinator));
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
395
|
+
if complex_selectors.is_empty() {
|
|
396
|
+
bail!("Failed to transform one off global selector");
|
|
397
|
+
}
|
|
317
398
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
sel.subclass_selectors
|
|
322
|
-
.extend(node.subclass_selectors[i + 1..].iter().cloned());
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
});
|
|
399
|
+
trace!("Combinator: {:?}", combinator);
|
|
400
|
+
// trace!("node: {:?}", node);
|
|
401
|
+
// trace!("complex_selectors: {:?}", complex_selectors);
|
|
326
402
|
|
|
327
|
-
|
|
403
|
+
// result.push(Component::Combinator(Combinator::Descendant));
|
|
328
404
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
405
|
+
result.extend(complex_selectors);
|
|
406
|
+
result.extend(node.into_iter().skip(i + 1));
|
|
407
|
+
|
|
408
|
+
if let Some(combinator) = combinator {
|
|
409
|
+
result.push(Component::Combinator(combinator));
|
|
333
410
|
}
|
|
411
|
+
|
|
412
|
+
return Ok(result);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Pseudo element
|
|
416
|
+
if result.is_empty() && node.len() == 1 && pseudo_index.is_some() {
|
|
417
|
+
return Ok(node);
|
|
334
418
|
}
|
|
335
419
|
|
|
420
|
+
let mut node: Vec<Component<'i>> = node.clone();
|
|
421
|
+
|
|
336
422
|
let subclass_selector = match self.is_dynamic {
|
|
337
|
-
true => "__jsx-style-dynamic-selector",
|
|
338
|
-
false =>
|
|
339
|
-
};
|
|
340
|
-
let insert_index = match pseudo_index {
|
|
341
|
-
None => node.subclass_selectors.len(),
|
|
342
|
-
Some(i) => i,
|
|
423
|
+
true => Cow::Borrowed("__jsx-style-dynamic-selector"),
|
|
424
|
+
false => Cow::Owned(self.class_name.clone()),
|
|
343
425
|
};
|
|
426
|
+
match pseudo_index {
|
|
427
|
+
None => {
|
|
428
|
+
if !self.is_global {
|
|
429
|
+
node.push(Component::Class(Ident::from(subclass_selector)));
|
|
430
|
+
}
|
|
344
431
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
insert_index,
|
|
348
|
-
SubclassSelector::Class(ClassSelector {
|
|
349
|
-
span: DUMMY_SP,
|
|
350
|
-
text: Ident {
|
|
351
|
-
raw: Some(subclass_selector.into()),
|
|
352
|
-
value: subclass_selector.into(),
|
|
353
|
-
span: DUMMY_SP,
|
|
354
|
-
},
|
|
355
|
-
}),
|
|
356
|
-
);
|
|
357
|
-
}
|
|
432
|
+
result.extend(node);
|
|
433
|
+
}
|
|
358
434
|
|
|
359
|
-
|
|
435
|
+
Some(insert_index) => {
|
|
436
|
+
result.extend(node);
|
|
360
437
|
|
|
361
|
-
|
|
362
|
-
|
|
438
|
+
if !self.is_global {
|
|
439
|
+
result.insert(
|
|
440
|
+
insert_index,
|
|
441
|
+
Component::Class(Ident::from(subclass_selector)),
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
363
445
|
}
|
|
364
446
|
|
|
365
|
-
|
|
447
|
+
if let Some(combinator) = combinator {
|
|
448
|
+
result.push(Component::Combinator(combinator));
|
|
449
|
+
}
|
|
366
450
|
|
|
367
451
|
Ok(result)
|
|
368
452
|
}
|
|
369
453
|
}
|
|
370
454
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
455
|
+
/// Because it is allowed to write `.bar :global(> .foo) {}` or .bar
|
|
456
|
+
/// :global(.foo) {}`, so selector can be complex or relative (it violates the
|
|
457
|
+
/// specification), but it is popular usage, so we just add `a ` at top and
|
|
458
|
+
/// then remove it
|
|
459
|
+
fn parse_token_list<'i>(tokens: &TokenList<'i>) -> Selector<'i> {
|
|
460
|
+
let mut buf = "a ".to_string();
|
|
461
|
+
|
|
462
|
+
for t in tokens.0.iter() {
|
|
463
|
+
match t {
|
|
464
|
+
TokenOrValue::Token(t) => {
|
|
465
|
+
buf.push_str(&t.to_css_string(Default::default()).unwrap());
|
|
466
|
+
}
|
|
467
|
+
TokenOrValue::Color(t) => {
|
|
468
|
+
buf.push_str(&t.to_css_string(Default::default()).unwrap());
|
|
469
|
+
}
|
|
470
|
+
TokenOrValue::Url(t) => {
|
|
471
|
+
buf.push_str(&t.to_css_string(Default::default()).unwrap());
|
|
472
|
+
}
|
|
473
|
+
TokenOrValue::Var(..) => {
|
|
474
|
+
unimplemented!("parse_token_list: var")
|
|
475
|
+
}
|
|
476
|
+
TokenOrValue::Env(..) => {
|
|
477
|
+
unimplemented!("parse_token_list: env var")
|
|
478
|
+
}
|
|
479
|
+
TokenOrValue::Function(..) => {
|
|
480
|
+
unimplemented!("parse_token_list: function")
|
|
481
|
+
}
|
|
482
|
+
TokenOrValue::Length(t) => {
|
|
483
|
+
buf.push_str(&t.to_css_string(Default::default()).unwrap());
|
|
484
|
+
}
|
|
485
|
+
TokenOrValue::Angle(t) => {
|
|
486
|
+
buf.push_str(&t.to_css_string(Default::default()).unwrap());
|
|
487
|
+
}
|
|
488
|
+
TokenOrValue::Time(t) => {
|
|
489
|
+
buf.push_str(&t.to_css_string(Default::default()).unwrap());
|
|
490
|
+
}
|
|
491
|
+
TokenOrValue::Resolution(t) => {
|
|
492
|
+
buf.push_str(&t.to_css_string(Default::default()).unwrap());
|
|
493
|
+
}
|
|
494
|
+
TokenOrValue::DashedIdent(t) => {
|
|
495
|
+
buf.push_str(&t.to_css_string(Default::default()).unwrap());
|
|
496
|
+
}
|
|
497
|
+
TokenOrValue::UnresolvedColor(..) => {
|
|
498
|
+
unimplemented!("parse_token_list: unresolved color")
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
let selector = Selector::parse_string_with_options(&buf, Default::default())
|
|
503
|
+
.expect("failed to parse selector list");
|
|
504
|
+
|
|
505
|
+
unsafe {
|
|
506
|
+
// Safety: Selector is variant over 'i
|
|
507
|
+
transmute::<Selector, Selector>(owned_selector(&selector))
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
struct SafeDebug<'a>(&'a dyn Debug);
|
|
512
|
+
|
|
513
|
+
impl Debug for SafeDebug<'_> {
|
|
514
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
515
|
+
let s = catch_unwind(AssertUnwindSafe(|| format!("{:?}", self.0)));
|
|
516
|
+
|
|
517
|
+
match s {
|
|
518
|
+
Ok(s) => {
|
|
519
|
+
write!(f, "{}", s)
|
|
520
|
+
}
|
|
521
|
+
Err(_) => write!(f, "<panicked>"),
|
|
522
|
+
}
|
|
523
|
+
}
|
|
394
524
|
}
|