@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.
Files changed (31) hide show
  1. package/Cargo.toml +3 -8
  2. package/package.json +1 -1
  3. package/swc_plugin_styled_jsx.wasm +0 -0
  4. package/transform/Cargo.toml +10 -9
  5. package/transform/src/lib.rs +1 -0
  6. package/transform/src/lifetime.rs +408 -0
  7. package/transform/src/style.rs +3 -0
  8. package/transform/src/transform_css.rs +390 -260
  9. package/transform/src/visitor.rs +14 -1
  10. package/transform/tests/errors/ts-with-css-resolve/output.js +1 -1
  11. package/transform/tests/fixture/css-selector-after-pseudo/output.js +1 -1
  12. package/transform/tests/fixture/fragment/output.js +1 -1
  13. package/transform/tests/fixture/global/output.js +1 -1
  14. package/transform/tests/fixture/global-redundant/output.js +1 -1
  15. package/transform/tests/fixture/issue-30480/input.js +5 -1
  16. package/transform/tests/fixture/issue-30480/output.js +3 -3
  17. package/transform/tests/fixture/issue-30570/output.js +1 -1
  18. package/transform/tests/fixture/styles/output.js +4 -4
  19. package/transform/tests/fixture/tpl-placeholder-1-as-property/output.js +1 -2
  20. package/transform/tests/fixture/tpl-placeholder-4-as-part-of-value-in-multiple/output.js +1 -1
  21. package/transform/tests/fixture/transform-css/input.js +22 -22
  22. package/transform/tests/fixture/transform-css/output.js +3 -3
  23. package/transform/tests/fixture/transform-css-complex-selector/output.js +1 -1
  24. package/transform/tests/fixture/transform-css-complex-selector-2/input.js +12 -0
  25. package/transform/tests/fixture/transform-css-complex-selector-2/output.js +8 -0
  26. package/transform/tests/fixture/transform-css-global/output.js +1 -1
  27. package/transform/tests/fixture/transform-css-media-query/output.js +1 -1
  28. package/transform/tests/fixture/transform-css-normal/output.js +1 -1
  29. package/transform/tests/fixture/transform-css-nth-1/output.js +1 -1
  30. /package/transform/tests/fixture/{expressions → .expressions}/input.js +0 -0
  31. /package/transform/tests/fixture/{expressions → .expressions}/output.js +0 -0
@@ -1,40 +1,39 @@
1
- use std::{panic, sync::Arc};
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
- errors::HANDLER, source_map::Pos, util::take::Take, BytePos, SourceMap, Span, Spanned,
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
- // TODO use `parse_string_input` in future
47
- let config = ParserConfig {
48
- allow_wrong_line_comments: true,
49
- css_modules: false,
50
- ..Default::default()
51
- };
52
- let lexer = Lexer::new(
53
- StringInput::new(
54
- &style_info.css,
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(err) => {
57
+ Err(_err) => {
66
58
  HANDLER.with(|handler| {
67
59
  // Print css parsing errors
68
- err.to_diagnostics(handler).emit();
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
- // ? Do we need to support optionally prefixing?
84
- ss.visit_mut_with(&mut prefixer(Default::default()));
85
- ss.visit_mut_with(&mut Namespacer {
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 mut s = String::new();
95
- {
96
- let mut wr = BasicCssWriter::new(&mut s, None, BasicCssWriterConfig::default());
97
- let mut gen = CodeGenerator::new(&mut wr, CodegenConfig { minify: true });
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
- gen.emit(&ss).unwrap();
100
- }
102
+ debug!("Transformed CSS: \n{}", res.code);
101
103
 
102
104
  if style_info.expressions.is_empty() {
103
- return Ok(string_literal_expr(&s));
105
+ return Ok(string_literal_expr(&res.code));
104
106
  }
105
107
 
106
- let mut parts: Vec<&str> = s.split("__styled-jsx-placeholder-").collect();
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 substr = &i[(num_len + 2)..];
112
- *i = substr;
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
- /// Returns `(length, value)`
133
- fn read_number(s: &str) -> (usize, usize) {
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 value = s[0..idx].parse().expect("failed to parse");
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, value);
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 Namespacer {
175
+ struct CssNamespace {
149
176
  class_name: String,
150
177
  is_global: bool,
151
178
  is_dynamic: bool,
152
179
  }
153
180
 
154
- impl VisitMut for Namespacer {
155
- fn visit_mut_complex_selector(&mut self, node: &mut ComplexSelector) {
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
- let mut code = String::new();
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
- gen.emit(&*node).unwrap();
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
- tracing::span!(
170
- tracing::Level::TRACE,
171
- "Namespacer::visit_mut_complex_selector",
172
- class_name = &*self.class_name,
173
- is_global = self.is_global,
174
- is_dynamic = self.is_dynamic,
175
- input = &*code
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
- let mut new_selectors = vec![];
181
- let mut combinator = None;
208
+ if combinator.is_none() {
209
+ combinator = iter.next_sequence();
210
+ }
182
211
 
183
- for sel in node.children.take() {
184
- match &sel {
185
- ComplexSelectorChildren::CompoundSelector(selector) => {
186
- match self.get_transformed_selectors(combinator, selector.clone()) {
187
- Ok(transformed_selectors) => new_selectors.extend(transformed_selectors),
188
- Err(_) => {
189
- HANDLER.with(|handler| {
190
- handler
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
- combinator = None;
222
+ new_selectors.push(transformed_selectors);
202
223
  }
203
- ComplexSelectorChildren::Combinator(v) => {
204
- combinator = Some(v.clone());
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
- node.children = new_selectors;
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 Namespacer {
214
- fn get_transformed_selectors(
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
- mut node: CompoundSelector,
218
- ) -> Result<Vec<ComplexSelectorChildren>, Error> {
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
- for (i, selector) in node.subclass_selectors.iter().enumerate() {
222
- let (name, children) = match &selector {
223
- SubclassSelector::PseudoClass(PseudoClassSelector {
224
- name,
225
- children: Some(children),
226
- ..
227
- }) if &name.value == "global" => (name, children),
228
- SubclassSelector::PseudoClass(_) | SubclassSelector::PseudoElement(_) => {
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
- _ => continue,
373
+ _ => {
374
+ continue;
375
+ }
236
376
  };
237
377
 
238
- // One off global selector
239
- if &name.value == "global" {
240
- // TODO(alexander-akait): in future we should use list of component values
241
- let tokens = children
242
- .iter()
243
- .map(|v| match v {
244
- PseudoClassSelectorChildren::PreservedToken(v) => v.clone(),
245
- _ => {
246
- unreachable!();
247
- }
248
- })
249
- .collect::<Vec<TokenAndSpan>>();
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
- if v.is_empty() {
298
- bail!("Failed to transform one off global selector");
299
- }
391
+ if let Component::Combinator(Combinator::Descendant) = complex_selectors[0] {
392
+ complex_selectors.remove(0);
393
+ }
300
394
 
301
- trace!("Combinator: {:?}", combinator);
302
- trace!("v[0]: {:?}", v[0]);
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
- v.iter_mut().for_each(|sel| {
319
- if i < node.subclass_selectors.len() {
320
- if let ComplexSelectorChildren::CompoundSelector(sel) = sel {
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
- result.extend(v);
403
+ // result.push(Component::Combinator(Combinator::Descendant));
328
404
 
329
- Ok(result)
330
- }
331
- Err(_) => bail!("Failed to transform one off global selector"),
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 => &self.class_name,
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
- if !self.is_global {
346
- node.subclass_selectors.insert(
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
- let mut result = vec![];
435
+ Some(insert_index) => {
436
+ result.extend(node);
360
437
 
361
- if let Some(combinator) = combinator {
362
- result.push(ComplexSelectorChildren::Combinator(combinator));
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
- result.push(ComplexSelectorChildren::CompoundSelector(node));
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
- fn get_front_selector_tokens(selector_tokens: &Tokens) -> Vec<TokenAndSpan> {
372
- let start_pos = selector_tokens.span.lo.to_u32() - 2;
373
- vec![
374
- TokenAndSpan {
375
- span: Span {
376
- lo: BytePos(start_pos),
377
- hi: BytePos(start_pos + 1),
378
- ctxt: SyntaxContext::empty(),
379
- },
380
- token: Token::Ident {
381
- raw: "a".into(),
382
- value: "a".into(),
383
- },
384
- },
385
- TokenAndSpan {
386
- span: Span {
387
- lo: BytePos(start_pos + 1),
388
- hi: BytePos(start_pos + 2),
389
- ctxt: SyntaxContext::empty(),
390
- },
391
- token: Token::WhiteSpace { value: " ".into() },
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
  }