@swc/plugin-styled-jsx 1.3.0 → 1.5.0

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 (139) hide show
  1. package/Cargo.toml +23 -8
  2. package/package.json +1 -1
  3. package/src/lib.rs +9 -1
  4. package/src/style.rs +21 -0
  5. package/src/transform_css.rs +530 -0
  6. package/src/utils.rs +370 -0
  7. package/src/visitor.rs +921 -0
  8. package/swc_plugin_styled_jsx.wasm +0 -0
  9. package/tests/errors/nested-style-tags/input.js +31 -0
  10. package/tests/errors/nested-style-tags/output.js +31 -0
  11. package/tests/errors/nested-style-tags/output.stderr +21 -0
  12. package/tests/errors/no-child/input.js +6 -0
  13. package/tests/errors/no-child/output.js +8 -0
  14. package/tests/errors/no-child/output.stderr +7 -0
  15. package/tests/errors/ts-with-css-resolve/input.js +10 -0
  16. package/tests/errors/ts-with-css-resolve/output.js +5 -0
  17. package/tests/errors/ts-with-css-resolve/output.stderr +2 -0
  18. package/tests/errors/two-children/input.js +8 -0
  19. package/tests/errors/two-children/output.js +12 -0
  20. package/tests/errors/two-children/output.stderr +9 -0
  21. package/tests/errors/wrong-child-type/input.js +7 -0
  22. package/tests/errors/wrong-child-type/output.js +10 -0
  23. package/tests/errors/wrong-child-type/output.stderr +8 -0
  24. package/tests/errors/wrong-jsx-expression-type/input.js +7 -0
  25. package/tests/errors/wrong-jsx-expression-type/output.js +10 -0
  26. package/tests/errors/wrong-jsx-expression-type/output.stderr +8 -0
  27. package/tests/fixture/absent/input.js +6 -0
  28. package/tests/fixture/absent/output.js +7 -0
  29. package/tests/fixture/attribute-generation-classname-rewriting/input.js +55 -0
  30. package/tests/fixture/attribute-generation-classname-rewriting/output.js +112 -0
  31. package/tests/fixture/attribute-generation-modes/input.js +166 -0
  32. package/tests/fixture/attribute-generation-modes/output.js +228 -0
  33. package/tests/fixture/class/input.js +14 -0
  34. package/tests/fixture/class/output.js +12 -0
  35. package/tests/fixture/comments/input.js +18 -0
  36. package/tests/fixture/comments/output.js +12 -0
  37. package/tests/fixture/component-attribute/input.js +11 -0
  38. package/tests/fixture/component-attribute/output.js +10 -0
  39. package/tests/fixture/conflicts/input.js +16 -0
  40. package/tests/fixture/conflicts/output.js +14 -0
  41. package/tests/fixture/css-selector-after-pseudo/input.js +18 -0
  42. package/tests/fixture/css-selector-after-pseudo/output.js +11 -0
  43. package/tests/fixture/css-tag-same-file/input.js +25 -0
  44. package/tests/fixture/css-tag-same-file/output.js +21 -0
  45. package/tests/fixture/different-jsx-ids/input.js +23 -0
  46. package/tests/fixture/different-jsx-ids/output.js +24 -0
  47. package/tests/fixture/dynamic-element/input.js +56 -0
  48. package/tests/fixture/dynamic-element/output.js +40 -0
  49. package/tests/fixture/dynamic-element-class/input.js +32 -0
  50. package/tests/fixture/dynamic-element-class/output.js +25 -0
  51. package/tests/fixture/dynamic-element-external/input.js +12 -0
  52. package/tests/fixture/dynamic-element-external/output.js +12 -0
  53. package/tests/fixture/dynamic-this-in-arrow/input.js +41 -0
  54. package/tests/fixture/dynamic-this-in-arrow/output.js +39 -0
  55. package/tests/fixture/exported-jsx-style/input.js +1 -0
  56. package/tests/fixture/exported-jsx-style/output.js +1 -0
  57. package/tests/fixture/exported-non-jsx-style/input.js +1 -0
  58. package/tests/fixture/exported-non-jsx-style/output.js +1 -0
  59. package/tests/fixture/expressions/input.js +90 -0
  60. package/tests/fixture/expressions/output.js +91 -0
  61. package/tests/fixture/external-nested-scope/input.js +5 -0
  62. package/tests/fixture/external-nested-scope/output.js +7 -0
  63. package/tests/fixture/external-stylesheet/input.js +41 -0
  64. package/tests/fixture/external-stylesheet/output.js +34 -0
  65. package/tests/fixture/external-stylesheet-global/input.js +19 -0
  66. package/tests/fixture/external-stylesheet-global/output.js +18 -0
  67. package/tests/fixture/external-stylesheet-multi-line/input.js +9 -0
  68. package/tests/fixture/external-stylesheet-multi-line/output.js +9 -0
  69. package/tests/fixture/fragment/input.js +51 -0
  70. package/tests/fixture/fragment/output.js +47 -0
  71. package/tests/fixture/global/input.js +22 -0
  72. package/tests/fixture/global/output.js +7 -0
  73. package/tests/fixture/global-child-selector/input.js +10 -0
  74. package/tests/fixture/global-child-selector/output.js +8 -0
  75. package/tests/fixture/global-redundant/input.js +12 -0
  76. package/tests/fixture/global-redundant/output.js +10 -0
  77. package/tests/fixture/issue-30480/input.js +7 -0
  78. package/tests/fixture/issue-30480/output.js +15 -0
  79. package/tests/fixture/issue-30570/input.js +15 -0
  80. package/tests/fixture/issue-30570/output.js +12 -0
  81. package/tests/fixture/issue-31562-interpolation-in-mdea/input.js +30 -0
  82. package/tests/fixture/issue-31562-interpolation-in-mdea/output.js +43 -0
  83. package/tests/fixture/mixed-global-scoped/input.js +9 -0
  84. package/tests/fixture/mixed-global-scoped/output.js +11 -0
  85. package/tests/fixture/multiple-jsx/input.js +43 -0
  86. package/tests/fixture/multiple-jsx/output.js +32 -0
  87. package/tests/fixture/nested-style-tags/index.js +0 -0
  88. package/tests/fixture/nested-style-tags/output.js +0 -0
  89. package/tests/fixture/non-styled-jsx-style/input.js +7 -0
  90. package/tests/fixture/non-styled-jsx-style/output.js +12 -0
  91. package/tests/fixture/not-styled-jsx-tagged-templates/input.js +30 -0
  92. package/tests/fixture/not-styled-jsx-tagged-templates/output.js +24 -0
  93. package/tests/fixture/number-after-placeholder/input.js +17 -0
  94. package/tests/fixture/number-after-placeholder/output.js +17 -0
  95. package/tests/fixture/one-off-global-selectors/input.js +10 -0
  96. package/tests/fixture/one-off-global-selectors/output.js +8 -0
  97. package/tests/fixture/source-maps/input.js +8 -0
  98. package/tests/fixture/source-maps/output.js +8 -0
  99. package/tests/fixture/stateless/input.js +8 -0
  100. package/tests/fixture/stateless/output.js +12 -0
  101. package/tests/fixture/styles/input.js +57 -0
  102. package/tests/fixture/styles/output.js +39 -0
  103. package/tests/fixture/styles-external-invalid/input.js +13 -0
  104. package/tests/fixture/styles-external-invalid/output.js +9 -0
  105. package/tests/fixture/styles-external-invalid2/input.js +11 -0
  106. package/tests/fixture/styles-external-invalid2/output.js +8 -0
  107. package/tests/fixture/styles2/input.js +6 -0
  108. package/tests/fixture/styles2/output.js +5 -0
  109. package/tests/fixture/too-many/input.js +35 -0
  110. package/tests/fixture/too-many/output.js +49 -0
  111. package/tests/fixture/tpl-escape-1/input.js +14 -0
  112. package/tests/fixture/tpl-escape-1/output.js +12 -0
  113. package/tests/fixture/tpl-escape-2/input.js +17 -0
  114. package/tests/fixture/tpl-escape-2/output.js +17 -0
  115. package/tests/fixture/tpl-placeholder-1-as-property/input.js +17 -0
  116. package/tests/fixture/tpl-placeholder-1-as-property/output.js +29 -0
  117. package/tests/fixture/tpl-placeholder-2-as-part-of-value/input.js +21 -0
  118. package/tests/fixture/tpl-placeholder-2-as-part-of-value/output.js +34 -0
  119. package/tests/fixture/tpl-placeholder-3-as-value/input.js +17 -0
  120. package/tests/fixture/tpl-placeholder-3-as-value/output.js +28 -0
  121. package/tests/fixture/tpl-placeholder-4-as-part-of-value-in-multiple/input.js +17 -0
  122. package/tests/fixture/tpl-placeholder-4-as-part-of-value-in-multiple/output.js +31 -0
  123. package/tests/fixture/tpl-placeholder-5-values-of-multiple-properties/input.js +16 -0
  124. package/tests/fixture/tpl-placeholder-5-values-of-multiple-properties/output.js +31 -0
  125. package/tests/fixture/transform-css/input.js +176 -0
  126. package/tests/fixture/transform-css/output.js +8 -0
  127. package/tests/fixture/transform-css-complex-selector/input.js +116 -0
  128. package/tests/fixture/transform-css-complex-selector/output.js +8 -0
  129. package/tests/fixture/transform-css-global/input.js +32 -0
  130. package/tests/fixture/transform-css-global/output.js +8 -0
  131. package/tests/fixture/transform-css-media-query/input.js +24 -0
  132. package/tests/fixture/transform-css-media-query/output.js +8 -0
  133. package/tests/fixture/transform-css-normal/input.js +22 -0
  134. package/tests/fixture/transform-css-normal/output.js +8 -0
  135. package/tests/fixture/transform-css-nth-1/input.js +10 -0
  136. package/tests/fixture/transform-css-nth-1/output.js +8 -0
  137. package/tests/fixture/whitespace/input.js +12 -0
  138. package/tests/fixture/whitespace/output.js +12 -0
  139. package/tests/fixture.rs +87 -0
package/Cargo.toml CHANGED
@@ -9,14 +9,29 @@ version = "0.3.0"
9
9
  [lib]
10
10
  crate-type = ["cdylib", "rlib"]
11
11
 
12
+ [features]
13
+ plugin = ["swc_core/plugin_transform"]
14
+ custom_transform = ["swc_core/common_concurrent"]
15
+
12
16
  [dependencies]
13
- styled_jsx = "0.19.0"
14
- swc_common = { version = "0.28.10", features = ["concurrent"] }
15
- swc_core = { version = "0.26.0", features = [
16
- "plugin_transform",
17
- "ecma_utils",
18
- "ecma_visit",
19
- "ecma_ast",
17
+ easy-error = "1.0.0"
18
+ tracing = "0.1.32"
19
+
20
+ swc_core = { version = "0.29.10", features = [
20
21
  "common",
22
+ "css_ast",
23
+ "css_codegen",
24
+ "css_parser",
25
+ "css_prefixer",
26
+ "css_visit",
27
+ "ecma_parser",
28
+ "ecma_minifier",
29
+ "ecma_utils",
30
+ "ecma_visit"
21
31
  ] }
22
- swc_plugin = "0.90.0"
32
+
33
+ [dev-dependencies]
34
+ testing = "0.31.4"
35
+ swc_core = { features = [
36
+ "testing_transform"
37
+ ], version = "0.29.10" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swc/plugin-styled-jsx",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "SWC plugin for styled-jsx",
5
5
  "main": "swc_plugin_styled_jsx.wasm",
6
6
  "scripts": {
package/src/lib.rs CHANGED
@@ -1,15 +1,23 @@
1
+ #![feature(box_patterns)]
2
+ #[cfg(feature = "plugin")]
1
3
  use swc_core::{
2
4
  common::{sync::Lrc, FileName, SourceMap},
3
5
  ecma::{ast::Program, visit::FoldWith},
4
6
  plugin::{plugin_transform, proxies::TransformPluginProgramMetadata},
5
7
  };
6
8
 
9
+ pub mod style;
10
+ mod transform_css;
11
+ mod utils;
12
+ pub mod visitor;
13
+
14
+ #[cfg(feature = "plugin")]
7
15
  #[plugin_transform]
8
16
  fn styled_jsx_plugin(program: Program, _: TransformPluginProgramMetadata) -> Program {
9
17
  // TODO(kdy1): This is wrong, but it does not use cm
10
18
  let cm = Lrc::new(SourceMap::default());
11
19
 
12
- let program = program.fold_with(&mut styled_jsx::styled_jsx(cm, FileName::Anon));
20
+ let program = program.fold_with(&mut visitor::styled_jsx(cm, FileName::Anon));
13
21
 
14
22
  program
15
23
  }
package/src/style.rs ADDED
@@ -0,0 +1,21 @@
1
+ use swc_core::{common::Span, ecma::ast::*};
2
+
3
+ pub struct ExternalStyle {
4
+ pub expr: Expr,
5
+ pub identifier: Ident,
6
+ pub is_global: bool,
7
+ }
8
+
9
+ pub struct LocalStyle {
10
+ pub hash: String,
11
+ pub css: String,
12
+ pub css_span: Span,
13
+ pub is_dynamic: bool,
14
+ #[allow(clippy::vec_box)]
15
+ pub expressions: Vec<Box<Expr>>,
16
+ }
17
+
18
+ pub enum JSXStyle {
19
+ Local(LocalStyle),
20
+ External(ExternalStyle),
21
+ }
@@ -0,0 +1,530 @@
1
+ use std::{panic, sync::Arc};
2
+
3
+ use easy_error::{bail, Error};
4
+ 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::{parse_str, parse_tokens, parser::ParserConfig},
16
+ prefixer::prefixer,
17
+ visit::{VisitMut, VisitMutWith},
18
+ },
19
+ ecma::{
20
+ ast::{Expr, Tpl, TplElement},
21
+ parser::StringInput,
22
+ },
23
+ };
24
+ use tracing::{debug, trace};
25
+
26
+ use crate::{
27
+ style::LocalStyle,
28
+ utils::{hash_string, string_literal_expr},
29
+ };
30
+
31
+ pub fn transform_css(
32
+ _cm: Arc<SourceMap>,
33
+ style_info: &LocalStyle,
34
+ is_global: bool,
35
+ class_name: &Option<String>,
36
+ ) -> Result<Expr, Error> {
37
+ debug!("CSS: \n{}", style_info.css);
38
+
39
+ let result: Result<Stylesheet, _> = parse_str(
40
+ &style_info.css,
41
+ style_info.css_span.lo,
42
+ style_info.css_span.hi,
43
+ ParserConfig {
44
+ allow_wrong_line_comments: true,
45
+ },
46
+ // We ignore errors because we inject placeholders for expressions which is
47
+ // not a valid css.
48
+ &mut vec![],
49
+ );
50
+ let mut ss = match result {
51
+ Ok(ss) => ss,
52
+ Err(err) => {
53
+ HANDLER.with(|handler| {
54
+ // Print css parsing errors
55
+ err.to_diagnostics(handler).emit();
56
+
57
+ // TODO(kdy1): We may print css so the user can see the error, and report it.
58
+
59
+ handler
60
+ .struct_span_err(
61
+ style_info.css_span,
62
+ "Failed to parse css in styled jsx component",
63
+ )
64
+ .note(&format!("Input to the css parser is {}", style_info.css))
65
+ .emit()
66
+ });
67
+ bail!("Failed to parse css");
68
+ }
69
+ };
70
+ // ? Do we need to support optionally prefixing?
71
+ ss.visit_mut_with(&mut prefixer(Default::default()));
72
+ ss.visit_mut_with(&mut Namespacer {
73
+ class_name: match class_name {
74
+ Some(s) => s.clone(),
75
+ None => format!("jsx-{}", &hash_string(&style_info.hash)),
76
+ },
77
+ is_global,
78
+ is_dynamic: style_info.is_dynamic,
79
+ });
80
+
81
+ let mut s = String::new();
82
+ {
83
+ let mut wr = BasicCssWriter::new(&mut s, None, BasicCssWriterConfig::default());
84
+ let mut gen = CodeGenerator::new(&mut wr, CodegenConfig { minify: true });
85
+
86
+ gen.emit(&ss).unwrap();
87
+ }
88
+
89
+ if style_info.expressions.is_empty() {
90
+ return Ok(string_literal_expr(&s));
91
+ }
92
+
93
+ let mut parts: Vec<&str> = s.split("__styled-jsx-placeholder-").collect();
94
+ let mut final_expressions = vec![];
95
+ for i in parts.iter_mut().skip(1) {
96
+ let (num_len, expression_index) = read_number(i);
97
+ final_expressions.push(style_info.expressions[expression_index].clone());
98
+ let substr = &i[(num_len + 2)..];
99
+ *i = substr;
100
+ }
101
+
102
+ Ok(Expr::Tpl(Tpl {
103
+ quasis: parts
104
+ .iter()
105
+ .map(|quasi| {
106
+ TplElement {
107
+ cooked: None, // ? Do we need cooked as well
108
+ raw: quasi.replace('`', "\\`").into(),
109
+ span: DUMMY_SP,
110
+ tail: false,
111
+ }
112
+ })
113
+ .collect(),
114
+ exprs: final_expressions,
115
+ span: DUMMY_SP,
116
+ }))
117
+ }
118
+
119
+ /// Returns `(length, value)`
120
+ fn read_number(s: &str) -> (usize, usize) {
121
+ for (idx, c) in s.char_indices() {
122
+ if c.is_ascii_digit() {
123
+ continue;
124
+ }
125
+
126
+ // For 10, we reach here after `0`.
127
+ let value = s[0..idx].parse().expect("failed to parse");
128
+
129
+ return (idx, value);
130
+ }
131
+
132
+ unreachable!("read_number(`{}`) is invalid because it is empty", s)
133
+ }
134
+
135
+ struct Namespacer {
136
+ class_name: String,
137
+ is_global: bool,
138
+ is_dynamic: bool,
139
+ }
140
+
141
+ impl VisitMut for Namespacer {
142
+ fn visit_mut_complex_selector(&mut self, node: &mut ComplexSelector) {
143
+ #[cfg(debug_assertions)]
144
+ let _tracing = {
145
+ // This will add information to the log messages, only for debug build.
146
+ // Note that we use cargo feature to remove all logging on production builds.
147
+
148
+ let mut code = String::new();
149
+ {
150
+ let mut wr = BasicCssWriter::new(&mut code, None, BasicCssWriterConfig::default());
151
+ let mut gen = CodeGenerator::new(&mut wr, CodegenConfig { minify: true });
152
+
153
+ gen.emit(&*node).unwrap();
154
+ }
155
+
156
+ tracing::span!(
157
+ tracing::Level::TRACE,
158
+ "Namespacer::visit_mut_complex_selector",
159
+ class_name = &*self.class_name,
160
+ is_global = self.is_global,
161
+ is_dynamic = self.is_dynamic,
162
+ input = &*code
163
+ )
164
+ .entered()
165
+ };
166
+
167
+ let mut new_selectors = vec![];
168
+ let mut combinator = None;
169
+ for sel in node.children.take() {
170
+ match &sel {
171
+ ComplexSelectorChildren::CompoundSelector(selector) => {
172
+ match self.get_transformed_selectors(combinator, selector.clone()) {
173
+ Ok(transformed_selectors) => new_selectors.extend(transformed_selectors),
174
+ Err(_) => {
175
+ HANDLER.with(|handler| {
176
+ handler
177
+ .struct_span_err(
178
+ selector.span,
179
+ "Failed to transform one off global selector",
180
+ )
181
+ .emit()
182
+ });
183
+ new_selectors.push(sel);
184
+ }
185
+ }
186
+
187
+ combinator = None;
188
+ }
189
+ ComplexSelectorChildren::Combinator(v) => {
190
+ combinator = Some(v.clone());
191
+ }
192
+ };
193
+ }
194
+
195
+ node.children = new_selectors;
196
+ }
197
+ }
198
+
199
+ impl Namespacer {
200
+ fn get_transformed_selectors(
201
+ &mut self,
202
+ combinator: Option<Combinator>,
203
+ mut node: CompoundSelector,
204
+ ) -> Result<Vec<ComplexSelectorChildren>, Error> {
205
+ let mut pseudo_index = None;
206
+ let mut arg_tokens;
207
+
208
+ for (i, selector) in node.subclass_selectors.iter().enumerate() {
209
+ let (name, args) = match selector {
210
+ SubclassSelector::PseudoClass(PseudoClassSelector { name, children, .. }) => {
211
+ arg_tokens = children
212
+ .iter()
213
+ .flatten()
214
+ .flat_map(|v| match v {
215
+ PseudoClassSelectorChildren::PreservedToken(v) => vec![v.clone()],
216
+ PseudoClassSelectorChildren::AnPlusB(an_plus_b) => match an_plus_b {
217
+ AnPlusB::Ident(v) => to_tokens(v).tokens,
218
+ AnPlusB::AnPlusBNotation(v) => to_tokens(v).tokens,
219
+ },
220
+ PseudoClassSelectorChildren::Ident(v) => to_tokens(v).tokens,
221
+ PseudoClassSelectorChildren::Str(v) => to_tokens(v).tokens,
222
+ PseudoClassSelectorChildren::Delimiter(v) => to_tokens(v).tokens,
223
+ PseudoClassSelectorChildren::SelectorList(v) => to_tokens(v).tokens,
224
+ PseudoClassSelectorChildren::CompoundSelectorList(v) => {
225
+ to_tokens(v).tokens
226
+ }
227
+ PseudoClassSelectorChildren::RelativeSelectorList(v) => {
228
+ to_tokens(v).tokens
229
+ }
230
+ PseudoClassSelectorChildren::CompoundSelector(v) => to_tokens(v).tokens,
231
+ PseudoClassSelectorChildren::ForgivingSelectorList(v) => {
232
+ to_tokens(v).tokens
233
+ }
234
+ PseudoClassSelectorChildren::ForgivingRelativeSelectorList(v) => {
235
+ to_tokens(v).tokens
236
+ }
237
+ })
238
+ .collect::<Vec<_>>();
239
+
240
+ (name, &arg_tokens)
241
+ }
242
+ SubclassSelector::PseudoElement(PseudoElementSelector {
243
+ name, children, ..
244
+ }) => {
245
+ arg_tokens = children
246
+ .iter()
247
+ .flatten()
248
+ .flat_map(|v| match v {
249
+ PseudoElementSelectorChildren::PreservedToken(v) => vec![v.clone()],
250
+ PseudoElementSelectorChildren::Ident(v) => to_tokens(v).tokens,
251
+ PseudoElementSelectorChildren::CompoundSelector(v) => {
252
+ to_tokens(v).tokens
253
+ }
254
+ })
255
+ .collect::<Vec<_>>();
256
+
257
+ (name, &arg_tokens)
258
+ }
259
+ _ => continue,
260
+ };
261
+
262
+ // One off global selector
263
+ if &name.value == "global" {
264
+ let args = args.clone();
265
+ let mut args = {
266
+ let lo = args.first().map(|v| v.span.lo).unwrap_or(BytePos(0));
267
+ let hi = args.last().map(|v| v.span.hi).unwrap_or(BytePos(0));
268
+
269
+ Tokens {
270
+ span: Span::new(lo, hi, Default::default()),
271
+ tokens: args,
272
+ }
273
+ };
274
+
275
+ let block_tokens = get_block_tokens(&args);
276
+ let mut front_tokens = get_front_selector_tokens(&args);
277
+ front_tokens.extend(args.tokens);
278
+ front_tokens.extend(block_tokens);
279
+ args.tokens = front_tokens;
280
+
281
+ let complex_selectors = panic::catch_unwind(|| {
282
+ let x: ComplexSelector = parse_tokens(
283
+ &args,
284
+ ParserConfig {
285
+ allow_wrong_line_comments: true,
286
+ },
287
+ // TODO(kdy1): We might be able to report syntax errors.
288
+ &mut vec![],
289
+ )
290
+ .unwrap();
291
+ x
292
+ });
293
+
294
+ return match complex_selectors {
295
+ Ok(complex_selectors) => {
296
+ let mut v = complex_selectors.children[1..].to_vec();
297
+
298
+ if let ComplexSelectorChildren::Combinator(Combinator {
299
+ value: CombinatorValue::Descendant,
300
+ ..
301
+ }) = v[0]
302
+ {
303
+ v.remove(0);
304
+ }
305
+
306
+ if v.is_empty() {
307
+ bail!("Failed to transform one off global selector");
308
+ }
309
+
310
+ trace!("Combinator: {:?}", combinator);
311
+ trace!("v[0]: {:?}", v[0]);
312
+
313
+ let mut result = vec![];
314
+
315
+ if let Some(combinator) = combinator {
316
+ match v.get(0) {
317
+ // `Descendant` combinator can't be the first because we removed it
318
+ // above
319
+ Some(ComplexSelectorChildren::Combinator(..))
320
+ if combinator.value == CombinatorValue::Descendant => {}
321
+ _ => {
322
+ result.push(ComplexSelectorChildren::Combinator(combinator));
323
+ }
324
+ }
325
+ }
326
+
327
+ v.iter_mut().for_each(|sel| {
328
+ if i < node.subclass_selectors.len() {
329
+ if let ComplexSelectorChildren::CompoundSelector(sel) = sel {
330
+ sel.subclass_selectors
331
+ .extend(node.subclass_selectors[i + 1..].iter().cloned());
332
+ }
333
+ }
334
+ });
335
+
336
+ result.extend(v);
337
+
338
+ Ok(result)
339
+ }
340
+ Err(_) => bail!("Failed to transform one off global selector"),
341
+ };
342
+ } else if pseudo_index.is_none() {
343
+ pseudo_index = Some(i);
344
+ }
345
+ }
346
+
347
+ let subclass_selector = match self.is_dynamic {
348
+ true => "__jsx-style-dynamic-selector",
349
+ false => &self.class_name,
350
+ };
351
+ let insert_index = match pseudo_index {
352
+ None => node.subclass_selectors.len(),
353
+ Some(i) => i,
354
+ };
355
+ if !self.is_global {
356
+ node.subclass_selectors.insert(
357
+ insert_index,
358
+ SubclassSelector::Class(ClassSelector {
359
+ span: DUMMY_SP,
360
+ text: Ident {
361
+ raw: Some(subclass_selector.into()),
362
+ value: subclass_selector.into(),
363
+ span: DUMMY_SP,
364
+ },
365
+ }),
366
+ );
367
+ }
368
+
369
+ let mut result = vec![];
370
+
371
+ if let Some(combinator) = combinator {
372
+ result.push(ComplexSelectorChildren::Combinator(combinator));
373
+ }
374
+
375
+ result.push(ComplexSelectorChildren::CompoundSelector(node));
376
+
377
+ Ok(result)
378
+ }
379
+ }
380
+
381
+ fn get_front_selector_tokens(selector_tokens: &Tokens) -> Vec<TokenAndSpan> {
382
+ let start_pos = selector_tokens.span.lo.to_u32() - 2;
383
+ vec![
384
+ TokenAndSpan {
385
+ span: Span {
386
+ lo: BytePos(start_pos),
387
+ hi: BytePos(start_pos + 1),
388
+ ctxt: SyntaxContext::empty(),
389
+ },
390
+ token: Token::Ident {
391
+ raw: "a".into(),
392
+ value: "a".into(),
393
+ },
394
+ },
395
+ TokenAndSpan {
396
+ span: Span {
397
+ lo: BytePos(start_pos + 1),
398
+ hi: BytePos(start_pos + 2),
399
+ ctxt: SyntaxContext::empty(),
400
+ },
401
+ token: Token::WhiteSpace { value: " ".into() },
402
+ },
403
+ ]
404
+ }
405
+
406
+ fn get_block_tokens(selector_tokens: &Tokens) -> Vec<TokenAndSpan> {
407
+ let start_pos = selector_tokens.span.hi.to_u32();
408
+ vec![
409
+ TokenAndSpan {
410
+ span: Span {
411
+ lo: BytePos(start_pos),
412
+ hi: BytePos(start_pos + 1),
413
+ ctxt: SyntaxContext::empty(),
414
+ },
415
+ token: Token::WhiteSpace { value: " ".into() },
416
+ },
417
+ TokenAndSpan {
418
+ span: Span {
419
+ lo: BytePos(start_pos + 1),
420
+ hi: BytePos(start_pos + 2),
421
+ ctxt: SyntaxContext::empty(),
422
+ },
423
+ token: Token::LBrace,
424
+ },
425
+ TokenAndSpan {
426
+ span: Span {
427
+ lo: BytePos(start_pos + 2),
428
+ hi: BytePos(start_pos + 3),
429
+ ctxt: SyntaxContext::empty(),
430
+ },
431
+ token: Token::WhiteSpace { value: " ".into() },
432
+ },
433
+ TokenAndSpan {
434
+ span: Span {
435
+ lo: BytePos(start_pos + 3),
436
+ hi: BytePos(start_pos + 8),
437
+ ctxt: SyntaxContext::empty(),
438
+ },
439
+ token: Token::Ident {
440
+ value: "color".into(),
441
+ raw: "color".into(),
442
+ },
443
+ },
444
+ TokenAndSpan {
445
+ span: Span {
446
+ lo: BytePos(start_pos + 8),
447
+ hi: BytePos(start_pos + 9),
448
+ ctxt: SyntaxContext::empty(),
449
+ },
450
+ token: Token::Colon,
451
+ },
452
+ TokenAndSpan {
453
+ span: Span {
454
+ lo: BytePos(start_pos + 9),
455
+ hi: BytePos(start_pos + 10),
456
+ ctxt: SyntaxContext::empty(),
457
+ },
458
+ token: Token::WhiteSpace { value: " ".into() },
459
+ },
460
+ TokenAndSpan {
461
+ span: Span {
462
+ lo: BytePos(start_pos + 10),
463
+ hi: BytePos(start_pos + 13),
464
+ ctxt: SyntaxContext::empty(),
465
+ },
466
+ token: Token::Ident {
467
+ value: "red".into(),
468
+ raw: "red".into(),
469
+ },
470
+ },
471
+ TokenAndSpan {
472
+ span: Span {
473
+ lo: BytePos(start_pos + 13),
474
+ hi: BytePos(start_pos + 14),
475
+ ctxt: SyntaxContext::empty(),
476
+ },
477
+ token: Token::Semi,
478
+ },
479
+ TokenAndSpan {
480
+ span: Span {
481
+ lo: BytePos(start_pos + 14),
482
+ hi: BytePos(start_pos + 15),
483
+ ctxt: SyntaxContext::empty(),
484
+ },
485
+ token: Token::WhiteSpace { value: " ".into() },
486
+ },
487
+ TokenAndSpan {
488
+ span: Span {
489
+ lo: BytePos(start_pos + 15),
490
+ hi: BytePos(start_pos + 16),
491
+ ctxt: SyntaxContext::empty(),
492
+ },
493
+ token: Token::RBrace,
494
+ },
495
+ TokenAndSpan {
496
+ span: Span {
497
+ lo: BytePos(start_pos + 16),
498
+ hi: BytePos(start_pos + 17),
499
+ ctxt: SyntaxContext::empty(),
500
+ },
501
+ token: Token::WhiteSpace { value: " ".into() },
502
+ },
503
+ ]
504
+ }
505
+
506
+ fn to_tokens<N: Spanned>(node: &N) -> Tokens
507
+ where
508
+ for<'aa> CodeGenerator<&'aa mut BasicCssWriter<'aa, &'aa mut std::string::String>>: Emit<N>,
509
+ {
510
+ let mut s = String::new();
511
+ {
512
+ let mut wr = BasicCssWriter::new(&mut s, None, BasicCssWriterConfig::default());
513
+ let mut gen = CodeGenerator::new(&mut wr, CodegenConfig { minify: true });
514
+
515
+ gen.emit(node).unwrap();
516
+ }
517
+
518
+ let span = node.span();
519
+ let lexer = swc_core::css::parser::lexer::Lexer::new(
520
+ StringInput::new(&s, span.lo, span.hi),
521
+ ParserConfig {
522
+ allow_wrong_line_comments: true,
523
+ },
524
+ );
525
+
526
+ Tokens {
527
+ span: Span::new(span.lo, span.hi, Default::default()),
528
+ tokens: lexer.collect(),
529
+ }
530
+ }