@swc/plugin-styled-jsx 1.2.12 → 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 +24 -9
  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/src/utils.rs ADDED
@@ -0,0 +1,370 @@
1
+ use std::{collections::hash_map::DefaultHasher, hash::Hasher};
2
+
3
+ use swc_core::{common::DUMMY_SP, ecma::ast::*};
4
+
5
+ use crate::style::{ExternalStyle, JSXStyle, LocalStyle};
6
+
7
+ fn tpl_element(value: &str) -> TplElement {
8
+ TplElement {
9
+ raw: value.into(),
10
+ cooked: None,
11
+ span: DUMMY_SP,
12
+ tail: false,
13
+ }
14
+ }
15
+
16
+ pub fn compute_class_names(
17
+ styles: &[JSXStyle],
18
+ style_import_name: &str,
19
+ ) -> (Option<String>, Option<Expr>) {
20
+ let mut static_class_name = None;
21
+ let mut external_jsx_id = None;
22
+ let mut static_hashes = vec![];
23
+ let mut dynamic_styles = vec![];
24
+ let mut external_styles = vec![];
25
+ for style_info in styles {
26
+ match style_info {
27
+ JSXStyle::Local(style_info) => {
28
+ if !style_info.is_dynamic {
29
+ static_hashes.push(style_info.hash.clone());
30
+ } else {
31
+ dynamic_styles.push(style_info);
32
+ }
33
+ }
34
+ JSXStyle::External(external) => {
35
+ if !external.is_global {
36
+ external_styles.push(external.expr.clone());
37
+ }
38
+ }
39
+ }
40
+ }
41
+
42
+ if !external_styles.is_empty() {
43
+ let mut quasis = vec![tpl_element("jsx-")];
44
+ for _i in 1..external_styles.len() {
45
+ quasis.push(tpl_element(" jsx-"))
46
+ }
47
+ quasis.push(tpl_element(""));
48
+ external_jsx_id = Some(Expr::Tpl(Tpl {
49
+ quasis,
50
+ exprs: external_styles
51
+ .iter()
52
+ .map(|external| Box::new(external.clone()))
53
+ .collect(),
54
+ span: DUMMY_SP,
55
+ }));
56
+ }
57
+
58
+ if !static_hashes.is_empty() {
59
+ static_class_name = Some(format!("jsx-{}", hash_string(&static_hashes.join(","))));
60
+ }
61
+
62
+ let dynamic_class_name = match dynamic_styles.len() {
63
+ 0 => None,
64
+ _ => Some(Expr::Call(CallExpr {
65
+ callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
66
+ obj: Box::new(Expr::Ident(Ident {
67
+ sym: style_import_name.into(),
68
+ span: DUMMY_SP,
69
+ optional: false,
70
+ })),
71
+ prop: MemberProp::Ident(Ident {
72
+ sym: "dynamic".into(),
73
+ span: DUMMY_SP,
74
+ optional: false,
75
+ }),
76
+ span: DUMMY_SP,
77
+ }))),
78
+ args: vec![ExprOrSpread {
79
+ expr: Box::new(Expr::Array(ArrayLit {
80
+ elems: dynamic_styles
81
+ .iter()
82
+ .map(|style_info| {
83
+ let hash_input = match &static_class_name {
84
+ Some(class_name) => format!("{}{}", style_info.hash, class_name),
85
+ None => style_info.hash.clone(),
86
+ };
87
+ Some(ExprOrSpread {
88
+ expr: Box::new(Expr::Array(ArrayLit {
89
+ elems: vec![
90
+ Some(ExprOrSpread {
91
+ expr: Box::new(string_literal_expr(&hash_string(
92
+ &hash_input,
93
+ ))),
94
+ spread: None,
95
+ }),
96
+ Some(ExprOrSpread {
97
+ expr: Box::new(Expr::Array(ArrayLit {
98
+ elems: style_info
99
+ .expressions
100
+ .iter()
101
+ .map(|expression| {
102
+ Some(ExprOrSpread {
103
+ expr: expression.clone(),
104
+ spread: None,
105
+ })
106
+ })
107
+ .collect(),
108
+ span: DUMMY_SP,
109
+ })),
110
+ spread: None,
111
+ }),
112
+ ],
113
+ span: DUMMY_SP,
114
+ })),
115
+ spread: None,
116
+ })
117
+ })
118
+ .collect(),
119
+ span: DUMMY_SP,
120
+ })),
121
+ spread: None,
122
+ }],
123
+ span: DUMMY_SP,
124
+ type_args: None,
125
+ })),
126
+ };
127
+
128
+ let class_name_expr = match (
129
+ static_class_name.clone(),
130
+ dynamic_class_name,
131
+ external_jsx_id,
132
+ ) {
133
+ (Some(static_class_name), Some(dynamic_class_name), Some(external_jsx_id)) => Some(add(
134
+ add(
135
+ external_jsx_id,
136
+ string_literal_expr(&format!(" {} ", static_class_name)),
137
+ ),
138
+ dynamic_class_name,
139
+ )),
140
+ (Some(static_class_name), Some(dynamic_class_name), None) => Some(add(
141
+ string_literal_expr(&format!("{} ", static_class_name)),
142
+ dynamic_class_name,
143
+ )),
144
+ (Some(static_class_name), None, Some(external_jsx_id)) => Some(add(
145
+ string_literal_expr(&format!("{} ", static_class_name)),
146
+ external_jsx_id,
147
+ )),
148
+ (None, Some(dynamic_class_name), Some(external_jsx_id)) => Some(add(
149
+ add(external_jsx_id, string_literal_expr(" ")),
150
+ dynamic_class_name,
151
+ )),
152
+ (Some(static_class_name), None, None) => Some(string_literal_expr(&static_class_name)),
153
+ (None, Some(dynamic_class_name), None) => Some(dynamic_class_name),
154
+ (None, None, Some(external_jsx_id)) => Some(external_jsx_id),
155
+ _ => None,
156
+ };
157
+
158
+ (static_class_name, class_name_expr)
159
+ }
160
+
161
+ pub fn make_external_styled_jsx_el(style: &ExternalStyle, style_import_name: &str) -> JSXElement {
162
+ let attrs = vec![JSXAttrOrSpread::JSXAttr(JSXAttr {
163
+ name: JSXAttrName::Ident(Ident {
164
+ sym: "id".into(),
165
+ span: DUMMY_SP,
166
+ optional: false,
167
+ }),
168
+ value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
169
+ expr: JSXExpr::Expr(Box::new(style.expr.clone())),
170
+ span: DUMMY_SP,
171
+ })),
172
+ span: DUMMY_SP,
173
+ })];
174
+ let opening = JSXOpeningElement {
175
+ name: JSXElementName::Ident(Ident {
176
+ sym: style_import_name.into(),
177
+ span: DUMMY_SP,
178
+ optional: false,
179
+ }),
180
+ attrs,
181
+ span: DUMMY_SP,
182
+ self_closing: false,
183
+ type_args: None,
184
+ };
185
+
186
+ let closing = Some(JSXClosingElement {
187
+ name: JSXElementName::Ident(Ident {
188
+ sym: style_import_name.into(),
189
+ span: DUMMY_SP,
190
+ optional: false,
191
+ }),
192
+ span: DUMMY_SP,
193
+ });
194
+
195
+ let children = vec![JSXElementChild::JSXExprContainer(JSXExprContainer {
196
+ expr: JSXExpr::Expr(Box::new(Expr::Ident(style.identifier.clone()))),
197
+ span: DUMMY_SP,
198
+ })];
199
+ JSXElement {
200
+ opening,
201
+ closing,
202
+ children,
203
+ span: DUMMY_SP,
204
+ }
205
+ }
206
+
207
+ pub fn make_local_styled_jsx_el(
208
+ style_info: &LocalStyle,
209
+ css_expr: Expr,
210
+ style_import_name: &str,
211
+ static_class_name: Option<&String>,
212
+ ) -> JSXElement {
213
+ let hash_input = match (&style_info.is_dynamic, &static_class_name) {
214
+ (true, Some(class_name)) => format!("{}{}", style_info.hash, class_name),
215
+ _ => style_info.hash.clone(),
216
+ };
217
+ let mut attrs = vec![JSXAttrOrSpread::JSXAttr(JSXAttr {
218
+ name: JSXAttrName::Ident(Ident {
219
+ sym: "id".into(),
220
+ span: DUMMY_SP,
221
+ optional: false,
222
+ }),
223
+ value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
224
+ expr: JSXExpr::Expr(Box::new(string_literal_expr(
225
+ hash_string(&hash_input).as_str(),
226
+ ))),
227
+ span: DUMMY_SP,
228
+ })),
229
+ span: DUMMY_SP,
230
+ })];
231
+
232
+ if style_info.is_dynamic {
233
+ attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr {
234
+ name: JSXAttrName::Ident(Ident {
235
+ sym: "dynamic".into(),
236
+ span: DUMMY_SP,
237
+ optional: false,
238
+ }),
239
+ value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
240
+ expr: JSXExpr::Expr(Box::new(Expr::Array(ArrayLit {
241
+ elems: style_info
242
+ .expressions
243
+ .iter()
244
+ .map(|expression| {
245
+ Some(ExprOrSpread {
246
+ expr: expression.clone(),
247
+ spread: None,
248
+ })
249
+ })
250
+ .collect(),
251
+ span: DUMMY_SP,
252
+ }))),
253
+ span: DUMMY_SP,
254
+ })),
255
+ span: DUMMY_SP,
256
+ }));
257
+ }
258
+
259
+ let opening = JSXOpeningElement {
260
+ name: JSXElementName::Ident(Ident {
261
+ sym: style_import_name.into(),
262
+ span: DUMMY_SP,
263
+ optional: false,
264
+ }),
265
+ attrs,
266
+ span: DUMMY_SP,
267
+ self_closing: false,
268
+ type_args: None,
269
+ };
270
+
271
+ let closing = Some(JSXClosingElement {
272
+ name: JSXElementName::Ident(Ident {
273
+ sym: style_import_name.into(),
274
+ span: DUMMY_SP,
275
+ optional: false,
276
+ }),
277
+ span: DUMMY_SP,
278
+ });
279
+
280
+ let children = vec![JSXElementChild::JSXExprContainer(JSXExprContainer {
281
+ expr: JSXExpr::Expr(Box::new(css_expr)),
282
+ span: DUMMY_SP,
283
+ })];
284
+ JSXElement {
285
+ opening,
286
+ closing,
287
+ children,
288
+ span: DUMMY_SP,
289
+ }
290
+ }
291
+
292
+ pub fn get_usable_import_specifier(_items: &[ModuleItem]) -> String {
293
+ // TODO
294
+ String::from("_JSXStyle")
295
+ }
296
+
297
+ pub fn styled_jsx_import_decl(style_import_name: &str) -> ModuleItem {
298
+ ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
299
+ asserts: None,
300
+ span: DUMMY_SP,
301
+ type_only: false,
302
+ specifiers: vec![ImportSpecifier::Default(ImportDefaultSpecifier {
303
+ local: Ident {
304
+ sym: style_import_name.into(),
305
+ span: DUMMY_SP,
306
+ optional: false,
307
+ },
308
+ span: DUMMY_SP,
309
+ })],
310
+ src: Box::new(Str {
311
+ span: DUMMY_SP,
312
+ value: "styled-jsx/style".into(),
313
+ raw: None,
314
+ }),
315
+ }))
316
+ }
317
+
318
+ // TODO: maybe use DJBHasher (need to implement)
319
+ pub fn hash_string(str: &str) -> String {
320
+ let mut hasher = DefaultHasher::new();
321
+ hasher.write(str.as_bytes());
322
+ let hash_result = hasher.finish();
323
+ format!("{:x}", hash_result)
324
+ }
325
+
326
+ pub fn string_literal_expr(str: &str) -> Expr {
327
+ let str = str.replace("\\`", "`");
328
+ Expr::Lit(Lit::Str(Str {
329
+ value: str.into(),
330
+ span: DUMMY_SP,
331
+ raw: None,
332
+ }))
333
+ }
334
+
335
+ pub fn ident(str: &str) -> Ident {
336
+ Ident {
337
+ sym: str.into(),
338
+ span: DUMMY_SP,
339
+ optional: false,
340
+ }
341
+ }
342
+
343
+ pub fn is_capitalized(word: &str) -> bool {
344
+ word.chars().next().unwrap().is_uppercase()
345
+ }
346
+
347
+ pub fn add(left: Expr, right: Expr) -> Expr {
348
+ binary_expr(BinaryOp::Add, left, right)
349
+ }
350
+
351
+ pub fn and(left: Expr, right: Expr) -> Expr {
352
+ binary_expr(BinaryOp::LogicalAnd, left, right)
353
+ }
354
+
355
+ pub fn or(left: Expr, right: Expr) -> Expr {
356
+ binary_expr(BinaryOp::LogicalOr, left, right)
357
+ }
358
+
359
+ pub fn not_eq(left: Expr, right: Expr) -> Expr {
360
+ binary_expr(BinaryOp::NotEq, left, right)
361
+ }
362
+
363
+ pub fn binary_expr(op: BinaryOp, left: Expr, right: Expr) -> Expr {
364
+ Expr::Bin(BinExpr {
365
+ op,
366
+ left: Box::new(left),
367
+ right: Box::new(right),
368
+ span: DUMMY_SP,
369
+ })
370
+ }