swc-plugin-component-annotate 1.12.0 → 1.14.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swc-plugin-component-annotate",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "description": "Use SWC to automatically annotate React components with data attributes for component tracking",
5
5
  "author": "scttcper <scttcper@gmail.com>",
6
6
  "license": "MIT",
@@ -8,11 +8,6 @@
8
8
  "swc-plugin",
9
9
  "swc"
10
10
  ],
11
- "scripts": {
12
- "build": "cargo build --release --target wasm32-unknown-unknown",
13
- "test": "cargo test",
14
- "prepack": "cp -rf target/wasm32-unknown-unknown/release/swc_plugin_component_annotate.wasm ."
15
- },
16
11
  "main": "swc_plugin_component_annotate.wasm",
17
12
  "files": [
18
13
  "src",
@@ -28,7 +23,6 @@
28
23
  },
29
24
  "devDependencies": {},
30
25
  "peerDependencies": {},
31
- "packageManager": "pnpm@10.18.3",
32
26
  "release": {
33
27
  "branches": [
34
28
  "main"
@@ -36,5 +30,9 @@
36
30
  },
37
31
  "publishConfig": {
38
32
  "access": "public"
33
+ },
34
+ "scripts": {
35
+ "build": "cargo build --release --target wasm32-unknown-unknown",
36
+ "test": "cargo test"
39
37
  }
40
- }
38
+ }
package/src/constants.rs CHANGED
@@ -1,125 +1,130 @@
1
1
  use rustc_hash::FxHashSet;
2
+ use std::sync::OnceLock;
2
3
 
3
- pub fn default_ignored_elements() -> FxHashSet<&'static str> {
4
- let mut set = FxHashSet::default();
5
- let elements = [
6
- "a",
7
- "abbr",
8
- "address",
9
- "area",
10
- "article",
11
- "aside",
12
- "audio",
13
- "b",
14
- "base",
15
- "bdi",
16
- "bdo",
17
- "blockquote",
18
- "body",
19
- "br",
20
- "button",
21
- "canvas",
22
- "caption",
23
- "cite",
24
- "code",
25
- "col",
26
- "colgroup",
27
- "data",
28
- "datalist",
29
- "dd",
30
- "del",
31
- "details",
32
- "dfn",
33
- "dialog",
34
- "div",
35
- "dl",
36
- "dt",
37
- "em",
38
- "embed",
39
- "fieldset",
40
- "figure",
41
- "footer",
42
- "form",
43
- "h1",
44
- "h2",
45
- "h3",
46
- "h4",
47
- "h5",
48
- "h6",
49
- "head",
50
- "header",
51
- "hgroup",
52
- "hr",
53
- "html",
54
- "i",
55
- "iframe",
56
- "img",
57
- "input",
58
- "ins",
59
- "kbd",
60
- "keygen",
61
- "label",
62
- "legend",
63
- "li",
64
- "link",
65
- "main",
66
- "map",
67
- "mark",
68
- "menu",
69
- "menuitem",
70
- "meter",
71
- "nav",
72
- "noscript",
73
- "object",
74
- "ol",
75
- "optgroup",
76
- "option",
77
- "output",
78
- "p",
79
- "param",
80
- "pre",
81
- "progress",
82
- "q",
83
- "rb",
84
- "rp",
85
- "rt",
86
- "rtc",
87
- "ruby",
88
- "s",
89
- "samp",
90
- "script",
91
- "section",
92
- "select",
93
- "small",
94
- "source",
95
- "span",
96
- "strong",
97
- "style",
98
- "sub",
99
- "summary",
100
- "sup",
101
- "table",
102
- "tbody",
103
- "td",
104
- "template",
105
- "textarea",
106
- "tfoot",
107
- "th",
108
- "thead",
109
- "time",
110
- "title",
111
- "tr",
112
- "track",
113
- "u",
114
- "ul",
115
- "var",
116
- "video",
117
- "wbr",
118
- ];
4
+ pub fn default_ignored_elements() -> &'static FxHashSet<&'static str> {
5
+ static SET: OnceLock<FxHashSet<&'static str>> = OnceLock::new();
119
6
 
120
- for element in elements {
121
- set.insert(element);
122
- }
7
+ SET.get_or_init(|| {
8
+ let mut set = FxHashSet::default();
9
+ let elements = [
10
+ "a",
11
+ "abbr",
12
+ "address",
13
+ "area",
14
+ "article",
15
+ "aside",
16
+ "audio",
17
+ "b",
18
+ "base",
19
+ "bdi",
20
+ "bdo",
21
+ "blockquote",
22
+ "body",
23
+ "br",
24
+ "button",
25
+ "canvas",
26
+ "caption",
27
+ "cite",
28
+ "code",
29
+ "col",
30
+ "colgroup",
31
+ "data",
32
+ "datalist",
33
+ "dd",
34
+ "del",
35
+ "details",
36
+ "dfn",
37
+ "dialog",
38
+ "div",
39
+ "dl",
40
+ "dt",
41
+ "em",
42
+ "embed",
43
+ "fieldset",
44
+ "figure",
45
+ "footer",
46
+ "form",
47
+ "h1",
48
+ "h2",
49
+ "h3",
50
+ "h4",
51
+ "h5",
52
+ "h6",
53
+ "head",
54
+ "header",
55
+ "hgroup",
56
+ "hr",
57
+ "html",
58
+ "i",
59
+ "iframe",
60
+ "img",
61
+ "input",
62
+ "ins",
63
+ "kbd",
64
+ "keygen",
65
+ "label",
66
+ "legend",
67
+ "li",
68
+ "link",
69
+ "main",
70
+ "map",
71
+ "mark",
72
+ "menu",
73
+ "menuitem",
74
+ "meter",
75
+ "nav",
76
+ "noscript",
77
+ "object",
78
+ "ol",
79
+ "optgroup",
80
+ "option",
81
+ "output",
82
+ "p",
83
+ "param",
84
+ "pre",
85
+ "progress",
86
+ "q",
87
+ "rb",
88
+ "rp",
89
+ "rt",
90
+ "rtc",
91
+ "ruby",
92
+ "s",
93
+ "samp",
94
+ "script",
95
+ "section",
96
+ "select",
97
+ "small",
98
+ "source",
99
+ "span",
100
+ "strong",
101
+ "style",
102
+ "sub",
103
+ "summary",
104
+ "sup",
105
+ "table",
106
+ "tbody",
107
+ "td",
108
+ "template",
109
+ "textarea",
110
+ "tfoot",
111
+ "th",
112
+ "thead",
113
+ "time",
114
+ "title",
115
+ "tr",
116
+ "track",
117
+ "u",
118
+ "ul",
119
+ "var",
120
+ "video",
121
+ "wbr",
122
+ ];
123
123
 
124
- set
124
+ for element in elements {
125
+ set.insert(element);
126
+ }
127
+
128
+ set
129
+ })
125
130
  }
package/src/jsx_utils.rs CHANGED
@@ -6,16 +6,14 @@ use swc_core::ecma::ast::*;
6
6
  pub fn is_react_fragment(element: &JSXElementName) -> bool {
7
7
  match element {
8
8
  JSXElementName::Ident(ident) => ident.sym.as_ref() == "Fragment",
9
- JSXElementName::JSXMemberExpr(member_expr) => {
10
- // Check for React.Fragment
11
- if let JSXObject::Ident(obj) = &member_expr.obj {
12
- if obj.sym.as_ref() == "React" {
13
- return member_expr.prop.sym.as_ref() == "Fragment";
14
- }
15
- }
16
- false
17
- }
18
- _ => false,
9
+ JSXElementName::JSXMemberExpr(member_expr) => matches!(
10
+ &member_expr.obj,
11
+ JSXObject::Ident(obj)
12
+ if obj.sym.as_ref() == "React" && member_expr.prop.sym.as_ref() == "Fragment"
13
+ ),
14
+ JSXElementName::JSXNamespacedName(_) => false,
15
+ #[cfg(swc_ast_unknown)]
16
+ _ => panic!("unknown jsx element name"),
19
17
  }
20
18
  }
21
19
 
@@ -30,31 +28,49 @@ pub fn get_element_name(element: &JSXElementName) -> Cow<str> {
30
28
  JSXElementName::JSXNamespacedName(namespaced) => {
31
29
  Cow::Owned(format!("{}:{}", namespaced.ns.sym, namespaced.name.sym))
32
30
  }
31
+ #[cfg(swc_ast_unknown)]
32
+ _ => panic!("unknown jsx element name"),
33
33
  }
34
34
  }
35
35
 
36
36
  /// Recursively build the name for member expressions (e.g., "Components.UI.Button")
37
37
  fn get_member_expression_name(member_expr: &JSXMemberExpr) -> String {
38
- let obj_name = match &member_expr.obj {
39
- JSXObject::Ident(ident) => ident.sym.as_ref(),
40
- JSXObject::JSXMemberExpr(nested_member) => {
41
- return format!(
42
- "{}.{}",
43
- get_member_expression_name(nested_member),
44
- member_expr.prop.sym
45
- );
38
+ fn member_expression_name_len(member_expr: &JSXMemberExpr) -> usize {
39
+ let obj_len = match &member_expr.obj {
40
+ JSXObject::Ident(ident) => ident.sym.len(),
41
+ JSXObject::JSXMemberExpr(nested_member) => member_expression_name_len(nested_member),
42
+ #[cfg(swc_ast_unknown)]
43
+ _ => panic!("unknown jsx object"),
44
+ };
45
+
46
+ obj_len + 1 + member_expr.prop.sym.len()
47
+ }
48
+
49
+ fn push_member_expression_name(target: &mut String, member_expr: &JSXMemberExpr) {
50
+ match &member_expr.obj {
51
+ JSXObject::Ident(ident) => target.push_str(ident.sym.as_ref()),
52
+ JSXObject::JSXMemberExpr(nested_member) => {
53
+ push_member_expression_name(target, nested_member);
54
+ }
55
+ #[cfg(swc_ast_unknown)]
56
+ _ => panic!("unknown jsx object"),
46
57
  }
47
- };
48
58
 
49
- format!("{}.{}", obj_name, member_expr.prop.sym)
59
+ target.push('.');
60
+ target.push_str(member_expr.prop.sym.as_ref());
61
+ }
62
+
63
+ let mut output = String::with_capacity(member_expression_name_len(member_expr));
64
+ push_member_expression_name(&mut output, member_expr);
65
+ output
50
66
  }
51
67
 
52
68
  /// Check if a JSX element already has an attribute with the given name
53
69
  #[inline]
54
70
  pub fn has_attribute(element: &JSXOpeningElement, attr_name: &str) -> bool {
55
71
  element.attrs.iter().any(|attr| {
56
- matches!(attr, JSXAttrOrSpread::JSXAttr(jsx_attr)
57
- if matches!(&jsx_attr.name, JSXAttrName::Ident(ident)
72
+ matches!(attr, JSXAttrOrSpread::JSXAttr(jsx_attr)
73
+ if matches!(&jsx_attr.name, JSXAttrName::Ident(ident)
58
74
  if ident.sym.as_ref() == attr_name))
59
75
  })
60
76
  }
@@ -72,3 +88,25 @@ pub fn create_jsx_attr(name: &str, value: &str) -> JSXAttrOrSpread {
72
88
  })),
73
89
  })
74
90
  }
91
+
92
+ #[inline]
93
+ pub fn create_jsx_attr_with_ident(name: &IdentName, value: &str) -> JSXAttrOrSpread {
94
+ JSXAttrOrSpread::JSXAttr(JSXAttr {
95
+ span: Default::default(),
96
+ name: JSXAttrName::Ident(name.clone()),
97
+ value: Some(JSXAttrValue::Str(Str {
98
+ span: Default::default(),
99
+ value: value.into(),
100
+ raw: None,
101
+ })),
102
+ })
103
+ }
104
+
105
+ #[inline]
106
+ pub fn create_jsx_attr_with_ident_and_str(name: &IdentName, value: &Str) -> JSXAttrOrSpread {
107
+ JSXAttrOrSpread::JSXAttr(JSXAttr {
108
+ span: Default::default(),
109
+ name: JSXAttrName::Ident(name.clone()),
110
+ value: Some(JSXAttrValue::Str(value.clone())),
111
+ })
112
+ }
package/src/lib.rs CHANGED
@@ -8,7 +8,7 @@ use jsx_utils::*;
8
8
  use path_utils::{extract_absolute_path, extract_filename};
9
9
  use rustc_hash::FxHashSet;
10
10
  use swc_core::{
11
- common::FileName,
11
+ common::{FileName, DUMMY_SP},
12
12
  ecma::{
13
13
  ast::*,
14
14
  visit::{noop_visit_mut_type, VisitMut, VisitMutWith},
@@ -21,31 +21,55 @@ use swc_core::{
21
21
 
22
22
  pub struct ReactComponentAnnotateVisitor {
23
23
  config: PluginConfig,
24
- source_file_name: Option<String>,
25
- source_file_path: Option<String>,
24
+ source_file_name: Option<Str>,
25
+ source_file_path: Option<Str>,
26
26
  current_component_name: Option<String>,
27
- ignored_elements: FxHashSet<&'static str>,
27
+ ignored_elements: &'static FxHashSet<&'static str>,
28
28
  ignored_components_set: FxHashSet<String>,
29
+ component_attr_ident: IdentName,
30
+ element_attr_ident: IdentName,
31
+ source_file_attr_ident: IdentName,
32
+ source_path_attr_ident: Option<IdentName>,
29
33
  /// Track the local identifier name for `styled` from @emotion/styled
30
34
  styled_import: Option<String>,
31
35
  }
32
36
 
33
37
  impl ReactComponentAnnotateVisitor {
34
38
  pub fn new(config: PluginConfig, filename: &FileName) -> Self {
35
- let source_file_name = extract_filename(filename);
36
- let source_file_path = extract_absolute_path(filename);
39
+ let source_file_name = extract_filename(filename).map(|value| Str {
40
+ span: DUMMY_SP,
41
+ value: value.into(),
42
+ raw: None,
43
+ });
44
+ let source_file_path = extract_absolute_path(filename).map(|value| Str {
45
+ span: DUMMY_SP,
46
+ value: value.into(),
47
+ raw: None,
48
+ });
37
49
 
38
50
  // Pre-compute ignored components set for O(1) lookups
39
51
  let ignored_components_set: FxHashSet<String> =
40
52
  config.ignored_components.iter().cloned().collect();
53
+ let component_attr_ident = IdentName::new(config.component_attr_name().into(), DUMMY_SP);
54
+ let element_attr_ident = IdentName::new(config.element_attr_name().into(), DUMMY_SP);
55
+ let source_file_attr_ident =
56
+ IdentName::new(config.source_file_attr_name().into(), DUMMY_SP);
57
+ let source_path_attr_ident = config
58
+ .source_path_attr
59
+ .as_ref()
60
+ .map(|_| IdentName::new(config.source_path_attr_name().into(), DUMMY_SP));
41
61
 
42
62
  Self {
63
+ component_attr_ident,
43
64
  config,
65
+ element_attr_ident,
66
+ ignored_elements: constants::default_ignored_elements(),
67
+ ignored_components_set,
44
68
  source_file_name,
69
+ source_file_attr_ident,
45
70
  source_file_path,
71
+ source_path_attr_ident,
46
72
  current_component_name: None,
47
- ignored_elements: constants::default_ignored_elements(),
48
- ignored_components_set,
49
73
  styled_import: None,
50
74
  }
51
75
  }
@@ -86,6 +110,8 @@ impl ReactComponentAnnotateVisitor {
86
110
  // Fragments are always transparent containers
87
111
  jsx_fragment.visit_mut_with(self);
88
112
  }
113
+ #[cfg(swc_ast_unknown)]
114
+ JSXElementChild::Unknown(..) => panic!("unknown jsx element child"),
89
115
  _ => {}
90
116
  }
91
117
  }
@@ -101,6 +127,8 @@ impl ReactComponentAnnotateVisitor {
101
127
  JSXElementChild::JSXFragment(jsx_fragment) => {
102
128
  jsx_fragment.visit_mut_with(self);
103
129
  }
130
+ #[cfg(swc_ast_unknown)]
131
+ JSXElementChild::Unknown(..) => panic!("unknown jsx element child"),
104
132
  _ => {}
105
133
  }
106
134
  }
@@ -109,11 +137,6 @@ impl ReactComponentAnnotateVisitor {
109
137
  fn add_attributes_to_element(&self, opening_element: &mut JSXOpeningElement) {
110
138
  let element_name = get_element_name(&opening_element.name);
111
139
 
112
- // Skip React fragments
113
- if is_react_fragment(&opening_element.name) {
114
- return;
115
- }
116
-
117
140
  // Check if component should be ignored
118
141
  if let Some(ref component_name) = self.current_component_name {
119
142
  if self.should_ignore_component(component_name) {
@@ -121,58 +144,71 @@ impl ReactComponentAnnotateVisitor {
121
144
  }
122
145
  }
123
146
 
124
- // Check if element should be ignored
125
147
  if self.should_ignore_component(&element_name) {
126
148
  return;
127
149
  }
128
150
 
129
151
  let is_ignored_html = self.should_ignore_element(&element_name);
130
-
131
- // Add element attribute (for non-HTML elements or when component name differs)
132
- if !is_ignored_html
152
+ let add_element_attr = !is_ignored_html
133
153
  && !has_attribute(opening_element, self.config.element_attr_name())
134
154
  && (self.config.component_attr_name() != self.config.element_attr_name()
135
- || self.current_component_name.is_none())
136
- {
137
- opening_element.attrs.push(create_jsx_attr(
138
- self.config.element_attr_name(),
155
+ || self.current_component_name.is_none());
156
+ let add_component_attr = self.current_component_name.is_some()
157
+ && !has_attribute(opening_element, self.config.component_attr_name());
158
+ let add_source_file_attr = self.source_file_name.is_some()
159
+ && (self.current_component_name.is_some() || !is_ignored_html)
160
+ && !has_attribute(opening_element, self.config.source_file_attr_name());
161
+ let add_source_path_attr = self.source_file_path.is_some()
162
+ && self.source_path_attr_ident.is_some()
163
+ && (self.current_component_name.is_some() || !is_ignored_html)
164
+ && !has_attribute(opening_element, self.config.source_path_attr_name());
165
+
166
+ let attr_count = usize::from(add_element_attr)
167
+ + usize::from(add_component_attr)
168
+ + usize::from(add_source_file_attr)
169
+ + usize::from(add_source_path_attr);
170
+
171
+ if attr_count > 0 {
172
+ opening_element.attrs.reserve(attr_count);
173
+ }
174
+
175
+ if add_element_attr {
176
+ opening_element.attrs.push(create_jsx_attr_with_ident(
177
+ &self.element_attr_ident,
139
178
  &element_name,
140
179
  ));
141
180
  }
142
181
 
143
- // Add component attribute (only for root elements)
144
- if let Some(ref component_name) = self.current_component_name {
145
- if !has_attribute(opening_element, self.config.component_attr_name()) {
146
- opening_element.attrs.push(create_jsx_attr(
147
- self.config.component_attr_name(),
182
+ if add_component_attr {
183
+ if let Some(ref component_name) = self.current_component_name {
184
+ opening_element.attrs.push(create_jsx_attr_with_ident(
185
+ &self.component_attr_ident,
148
186
  component_name,
149
187
  ));
150
188
  }
151
189
  }
152
190
 
153
- // Add source file attribute
154
- if let Some(ref source_file) = self.source_file_name {
155
- if (self.current_component_name.is_some() || !is_ignored_html)
156
- && !has_attribute(opening_element, self.config.source_file_attr_name())
157
- {
158
- opening_element.attrs.push(create_jsx_attr(
159
- self.config.source_file_attr_name(),
160
- source_file,
161
- ));
191
+ if add_source_file_attr {
192
+ if let Some(ref source_file) = self.source_file_name {
193
+ opening_element
194
+ .attrs
195
+ .push(create_jsx_attr_with_ident_and_str(
196
+ &self.source_file_attr_ident,
197
+ source_file,
198
+ ));
162
199
  }
163
200
  }
164
201
 
165
- // Add source path attribute (only if explicitly configured)
166
- if self.config.source_path_attr.is_some() {
167
- if let Some(ref source_path) = self.source_file_path {
168
- if (self.current_component_name.is_some() || !is_ignored_html)
169
- && !has_attribute(opening_element, self.config.source_path_attr_name())
170
- {
171
- opening_element.attrs.push(create_jsx_attr(
172
- self.config.source_path_attr_name(),
202
+ if add_source_path_attr {
203
+ if let (Some(ref source_path), Some(ref source_path_attr_ident)) =
204
+ (&self.source_file_path, &self.source_path_attr_ident)
205
+ {
206
+ opening_element
207
+ .attrs
208
+ .push(create_jsx_attr_with_ident_and_str(
209
+ source_path_attr_ident,
173
210
  source_path,
174
211
  ));
175
- }
176
212
  }
177
213
  }
178
214
  }
@@ -210,6 +246,8 @@ impl ReactComponentAnnotateVisitor {
210
246
  Expr::Paren(paren_expr) => {
211
247
  self.process_return_expression(&mut paren_expr.expr);
212
248
  }
249
+ #[cfg(swc_ast_unknown)]
250
+ Expr::Unknown(..) => panic!("unknown expr"),
213
251
  _ => {}
214
252
  }
215
253
  }
@@ -223,6 +261,8 @@ impl ReactComponentAnnotateVisitor {
223
261
  let callee_name = match call_expr.callee.as_expr() {
224
262
  Some(expr) => match expr.as_ref() {
225
263
  Expr::Ident(ident) => ident.sym.as_ref(),
264
+ #[cfg(swc_ast_unknown)]
265
+ Expr::Unknown(..) => panic!("unknown expr"),
226
266
  _ => return None,
227
267
  },
228
268
  _ => return None,
@@ -258,7 +298,12 @@ impl ReactComponentAnnotateVisitor {
258
298
  });
259
299
 
260
300
  // Build attributes in order: data attributes first, then spread
261
- let mut attrs = vec![];
301
+ let mut attrs = Vec::with_capacity(
302
+ 2 + usize::from(self.source_file_name.is_some())
303
+ + usize::from(
304
+ self.source_path_attr_ident.is_some() && self.source_file_path.is_some(),
305
+ ),
306
+ );
262
307
 
263
308
  // Add data-element attribute using the styled component variable name
264
309
  attrs.push(create_jsx_attr(
@@ -268,20 +313,20 @@ impl ReactComponentAnnotateVisitor {
268
313
 
269
314
  // Add data-source-file attribute
270
315
  if let Some(ref source_file) = self.source_file_name {
271
- attrs.push(create_jsx_attr(
272
- self.config.source_file_attr_name(),
316
+ attrs.push(create_jsx_attr_with_ident_and_str(
317
+ &self.source_file_attr_ident,
273
318
  source_file,
274
319
  ));
275
320
  }
276
321
 
277
322
  // Add data-source-path attribute (only if explicitly configured)
278
- if self.config.source_path_attr.is_some() {
279
- if let Some(ref source_path) = self.source_file_path {
280
- attrs.push(create_jsx_attr(
281
- self.config.source_path_attr_name(),
282
- source_path,
283
- ));
284
- }
323
+ if let (Some(ref source_path), Some(ref source_path_attr_ident)) =
324
+ (&self.source_file_path, &self.source_path_attr_ident)
325
+ {
326
+ attrs.push(create_jsx_attr_with_ident_and_str(
327
+ source_path_attr_ident,
328
+ source_path,
329
+ ));
285
330
  }
286
331
 
287
332
  // Add spread attribute AFTER data attributes: {...props}
@@ -352,16 +397,22 @@ impl VisitMut for ReactComponentAnnotateVisitor {
352
397
  ImportSpecifier::Named(named_import) => {
353
398
  // Check if the imported name is 'default' or 'styled'
354
399
  let imported_name = match &named_import.imported {
355
- Some(ModuleExportName::Ident(ident)) => ident.sym.as_ref(),
356
- None => named_import.local.sym.as_ref(),
357
- _ => continue,
400
+ Some(ModuleExportName::Ident(ident)) => Some(ident.sym.as_ref()),
401
+ Some(ModuleExportName::Str(str)) => str.value.as_str(),
402
+ None => Some(named_import.local.sym.as_ref()),
403
+ #[cfg(swc_ast_unknown)]
404
+ Some(_) => panic!("unknown module export name"),
358
405
  };
359
406
 
360
- if imported_name == "default" || imported_name == "styled" {
361
- self.styled_import = Some(named_import.local.sym.to_string());
407
+ if let Some(imported_name) = imported_name {
408
+ if imported_name == "default" || imported_name == "styled" {
409
+ self.styled_import = Some(named_import.local.sym.to_string());
410
+ }
362
411
  }
363
412
  }
364
- _ => {}
413
+ ImportSpecifier::Namespace(_) => {}
414
+ #[cfg(swc_ast_unknown)]
415
+ _ => panic!("unknown import specifier"),
365
416
  }
366
417
  }
367
418
  }
@@ -416,6 +467,8 @@ impl VisitMut for ReactComponentAnnotateVisitor {
416
467
  // Direct expression return
417
468
  self.process_return_expression(expr);
418
469
  }
470
+ #[cfg(swc_ast_unknown)]
471
+ _ => panic!("unknown block stmt or expr"),
419
472
  }
420
473
 
421
474
  self.current_component_name = None;
@@ -423,6 +476,8 @@ impl VisitMut for ReactComponentAnnotateVisitor {
423
476
  Expr::Fn(func_expr) => {
424
477
  self.find_jsx_in_function_body(&mut func_expr.function, component_name);
425
478
  }
479
+ #[cfg(swc_ast_unknown)]
480
+ Expr::Unknown(..) => panic!("unknown expr"),
426
481
  _ => {}
427
482
  }
428
483
  }
@@ -436,25 +491,33 @@ impl VisitMut for ReactComponentAnnotateVisitor {
436
491
 
437
492
  // Look for render method
438
493
  for member in &mut class_decl.class.body {
439
- if let ClassMember::Method(method) = member {
440
- if let PropName::Ident(ident) = &method.key {
441
- if ident.sym.as_ref() == "render" {
442
- if let Some(body) = &mut method.function.body {
443
- self.current_component_name = Some(component_name.clone());
444
-
445
- // Look for return statements
446
- for stmt in &mut body.stmts {
447
- if let Stmt::Return(return_stmt) = stmt {
448
- if let Some(arg) = &mut return_stmt.arg {
449
- self.process_return_expression(arg);
494
+ match member {
495
+ ClassMember::Method(method) => match &method.key {
496
+ PropName::Ident(ident) => {
497
+ if ident.sym.as_ref() == "render" {
498
+ if let Some(body) = &mut method.function.body {
499
+ self.current_component_name = Some(component_name.clone());
500
+
501
+ // Look for return statements
502
+ for stmt in &mut body.stmts {
503
+ if let Stmt::Return(return_stmt) = stmt {
504
+ if let Some(arg) = &mut return_stmt.arg {
505
+ self.process_return_expression(arg);
506
+ }
450
507
  }
451
508
  }
452
- }
453
509
 
454
- self.current_component_name = None;
510
+ self.current_component_name = None;
511
+ }
455
512
  }
456
513
  }
457
- }
514
+ #[cfg(swc_ast_unknown)]
515
+ PropName::Unknown(..) => panic!("unknown prop name"),
516
+ _ => {}
517
+ },
518
+ #[cfg(swc_ast_unknown)]
519
+ ClassMember::Unknown(..) => panic!("unknown class member"),
520
+ _ => {}
458
521
  }
459
522
  }
460
523
 
Binary file