rask-ui 0.8.1 → 0.9.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.
@@ -1,10 +1,18 @@
1
1
  import { VNode, Component, Props } from "inferno";
2
- export declare function getCurrentComponent(): RaskComponent<any>;
2
+ import { Observer } from "./observation";
3
+ export type RaskStatelessFunctionComponent<P extends Props<any>> = (() => VNode) | ((props: P) => VNode);
4
+ export declare class RaskStatelessComponent extends Component {
5
+ renderFn: RaskStatelessFunctionComponent<any>;
6
+ observer: Observer;
7
+ shouldComponentUpdate(nextProps: Props<any>): boolean;
8
+ render(): VNode;
9
+ }
10
+ export declare function getCurrentComponent(): RaskStatefulComponent<any>;
3
11
  export declare function createMountEffect(cb: () => void): void;
4
12
  export declare function createCleanup(cb: () => void): void;
5
- export type RaskFunctionComponent<P extends Props<any>> = (() => () => VNode) | ((props: P) => () => VNode);
6
- export declare class RaskComponent<P extends Props<any>> extends Component<P> {
7
- setup: RaskFunctionComponent<P>;
13
+ export type RaskStatefulFunctionComponent<P extends Props<any>> = (() => () => VNode) | ((props: P) => () => VNode);
14
+ export declare class RaskStatefulComponent<P extends Props<any>> extends Component<P> {
15
+ setup: RaskStatefulFunctionComponent<P>;
8
16
  private renderFn?;
9
17
  private reactiveProps?;
10
18
  private observer;
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EACL,SAAS,EACT,KAAK,EAEN,MAAM,SAAS,CAAC;AAQjB,wBAAgB,mBAAmB,uBAMlC;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,IAAI,QAM/C;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,IAAI,QAM3C;AAED,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAClD,CAAC,MAAM,MAAM,KAAK,CAAC,GACnB,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAAK,CAAC,CAAC;AAEhC,qBAAa,aAAa,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IAC3D,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACxC,OAAO,CAAC,QAAQ,CAAC,CAAc;IAC/B,OAAO,CAAC,aAAa,CAAC,CAAa;IACnC,OAAO,CAAC,QAAQ,CAEb;IACH,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC,CAAM;IAC3D,QAAQ,gBAAa;IACrB,eAAe;IAUf,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IACjC,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IACnC,OAAO,CAAC,mBAAmB;IA2D3B,iBAAiB,IAAI,IAAI;IAGzB,oBAAoB,IAAI,IAAI;IAG5B;;OAEG;IACH,mBAAmB,CAAC,SAAS,EAAE,GAAG;IAYlC,yBAAyB,IAAI,IAAI;IACjC,qBAAqB,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO;IAerD,MAAM;CAyCP;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,SAO9D"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EACL,SAAS,EACT,KAAK,EAEN,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsB,QAAQ,EAAU,MAAM,eAAe,CAAC;AAGrE,MAAM,MAAM,8BAA8B,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAC3D,CAAC,MAAM,KAAK,CAAC,GACb,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC;AAE1B,qBAAa,sBAAuB,SAAQ,SAAS;IAC3C,QAAQ,EAAE,8BAA8B,CAAC,GAAG,CAAC,CAAC;IACtD,QAAQ,WAEL;IACH,qBAAqB,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO;IAUrD,MAAM;CAMP;AAID,wBAAgB,mBAAmB,+BAMlC;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,IAAI,QAM/C;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,IAAI,QAM3C;AAED,MAAM,MAAM,6BAA6B,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAC1D,CAAC,MAAM,MAAM,KAAK,CAAC,GACnB,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAAK,CAAC,CAAC;AAEhC,qBAAa,qBAAqB,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IACnE,KAAK,EAAE,6BAA6B,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO,CAAC,QAAQ,CAAC,CAAc;IAC/B,OAAO,CAAC,aAAa,CAAC,CAAa;IACnC,OAAO,CAAC,QAAQ,CAEb;IACH,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC,CAAM;IAC3D,QAAQ,gBAAa;IACrB,eAAe;IAUf,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IACjC,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IACnC,OAAO,CAAC,mBAAmB;IA2D3B,iBAAiB,IAAI,IAAI;IAGzB,oBAAoB,IAAI,IAAI;IAG5B;;OAEG;IACH,mBAAmB,CAAC,SAAS,EAAE,GAAG;IAYlC,yBAAyB,IAAI,IAAI;IACjC,qBAAqB,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO;IAWrD,MAAM;CAyCP;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,SAO9D"}
package/dist/component.js CHANGED
@@ -1,6 +1,26 @@
1
1
  import { createComponentVNode, Component, } from "inferno";
2
2
  import { getCurrentObserver, Observer, Signal } from "./observation";
3
3
  import { syncBatch } from "./batch";
4
+ export class RaskStatelessComponent extends Component {
5
+ observer = new Observer(() => {
6
+ this.forceUpdate();
7
+ });
8
+ shouldComponentUpdate(nextProps) {
9
+ for (const prop in nextProps) {
10
+ // @ts-ignore
11
+ if (this.props[prop] !== nextProps[prop]) {
12
+ return true;
13
+ }
14
+ }
15
+ return false;
16
+ }
17
+ render() {
18
+ const stopObserving = this.observer.observe();
19
+ const result = this.renderFn(this.props);
20
+ stopObserving();
21
+ return result;
22
+ }
23
+ }
4
24
  let currentComponent;
5
25
  export function getCurrentComponent() {
6
26
  if (!currentComponent) {
@@ -20,7 +40,7 @@ export function createCleanup(cb) {
20
40
  }
21
41
  currentComponent.onCleanups.push(cb);
22
42
  }
23
- export class RaskComponent extends Component {
43
+ export class RaskStatefulComponent extends Component {
24
44
  renderFn;
25
45
  reactiveProps;
26
46
  observer = new Observer(() => {
@@ -113,9 +133,6 @@ export class RaskComponent extends Component {
113
133
  shouldComponentUpdate(nextProps) {
114
134
  // Shallow comparison of props, excluding internal props
115
135
  for (const prop in nextProps) {
116
- if (prop === "__component") {
117
- continue;
118
- }
119
136
  // @ts-ignore
120
137
  if (this.props[prop] !== nextProps[prop]) {
121
138
  return true;
@@ -162,5 +179,5 @@ export class RaskComponent extends Component {
162
179
  }
163
180
  }
164
181
  export function createComponent(props, key) {
165
- return createComponentVNode(4 /* VNodeFlags.ComponentClass */, RaskComponent, props, key);
182
+ return createComponentVNode(4 /* VNodeFlags.ComponentClass */, RaskStatefulComponent, props, key);
166
183
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { render } from "./render";
2
- export { createCleanup, createMountEffect, RaskComponent } from "./component";
2
+ export { createCleanup, createMountEffect, RaskStatefulComponent, RaskStatelessComponent, } from "./component";
3
3
  export { createContext } from "./createContext";
4
4
  export { createState } from "./createState";
5
5
  export { createTask } from "./createTask";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,cAAc,EACd,SAAS,GACV,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,cAAc,EACd,SAAS,GACV,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { render } from "./render";
2
- export { createCleanup, createMountEffect, RaskComponent } from "./component";
2
+ export { createCleanup, createMountEffect, RaskStatefulComponent, RaskStatelessComponent, } from "./component";
3
3
  export { createContext } from "./createContext";
4
4
  export { createState } from "./createState";
5
5
  export { createTask } from "./createTask";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rask-ui",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -15,14 +15,16 @@ pub struct Config {
15
15
 
16
16
  pub struct RaskComponentTransform {
17
17
  config: Config,
18
- import_rask_component: Option<Ident>,
18
+ import_rask_stateful_component: Option<Ident>,
19
+ import_rask_stateless_component: Option<Ident>,
19
20
  }
20
21
 
21
22
  impl RaskComponentTransform {
22
23
  fn new(config: Config) -> Self {
23
24
  RaskComponentTransform {
24
25
  config,
25
- import_rask_component: None,
26
+ import_rask_stateful_component: None,
27
+ import_rask_stateless_component: None,
26
28
  }
27
29
  }
28
30
 
@@ -100,7 +102,27 @@ impl RaskComponentTransform {
100
102
  }
101
103
  }
102
104
 
103
- /// Check if a function body returns an arrow function with VNode calls
105
+ /// Check if a function body directly returns VNode calls (stateless component)
106
+ fn is_stateless_component(&self, func: &Function) -> bool {
107
+ if let Some(body) = &func.body {
108
+ for stmt in &body.stmts {
109
+ if let Stmt::Return(ret_stmt) = stmt {
110
+ if let Some(ret_arg) = &ret_stmt.arg {
111
+ // Check if directly returning VNode (not arrow function)
112
+ if self.has_vnode_call(ret_arg) {
113
+ // Make sure it's NOT an arrow function
114
+ if !matches!(&**ret_arg, Expr::Arrow(_)) {
115
+ return true;
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ false
123
+ }
124
+
125
+ /// Check if a function body returns an arrow function with VNode calls (stateful component)
104
126
  fn is_rask_component(&self, func: &Function) -> bool {
105
127
  if let Some(body) = &func.body {
106
128
  for stmt in &body.stmts {
@@ -135,14 +157,14 @@ impl RaskComponentTransform {
135
157
  false
136
158
  }
137
159
 
138
- /// Transform a function declaration to a RaskComponent class
139
- fn transform_to_class(&mut self, name: Ident, func: Function) -> Decl {
140
- // Ensure we have the RaskComponent import
141
- if self.import_rask_component.is_none() {
142
- self.import_rask_component = Some(private_ident!("RaskComponent"));
160
+ /// Transform a function declaration to a RaskStatefulComponent class
161
+ fn transform_to_stateful_class(&mut self, name: Ident, func: Function) -> Decl {
162
+ // Ensure we have the RaskStatefulComponent import
163
+ if self.import_rask_stateful_component.is_none() {
164
+ self.import_rask_stateful_component = Some(private_ident!("RaskStatefulComponent"));
143
165
  }
144
166
 
145
- let super_class_ident = self.import_rask_component.as_ref().unwrap().clone();
167
+ let super_class_ident = self.import_rask_stateful_component.as_ref().unwrap().clone();
146
168
 
147
169
  // Create the class property: setup = function name() { ... }
148
170
  let setup_prop = ClassMember::ClassProp(ClassProp {
@@ -181,6 +203,52 @@ impl RaskComponentTransform {
181
203
  })
182
204
  }
183
205
 
206
+ /// Transform a function declaration to a RaskStatelessComponent class
207
+ fn transform_to_stateless_class(&mut self, name: Ident, func: Function) -> Decl {
208
+ // Ensure we have the RaskStatelessComponent import
209
+ if self.import_rask_stateless_component.is_none() {
210
+ self.import_rask_stateless_component = Some(private_ident!("RaskStatelessComponent"));
211
+ }
212
+
213
+ let super_class_ident = self.import_rask_stateless_component.as_ref().unwrap().clone();
214
+
215
+ // Create the class property: renderFn = function name() { ... }
216
+ let render_prop = ClassMember::ClassProp(ClassProp {
217
+ span: Default::default(),
218
+ key: PropName::Ident(quote_ident!("renderFn").into()),
219
+ value: Some(Box::new(Expr::Fn(FnExpr {
220
+ ident: Some(name.clone()),
221
+ function: Box::new(func),
222
+ }))),
223
+ type_ann: None,
224
+ is_static: false,
225
+ decorators: vec![],
226
+ accessibility: None,
227
+ is_abstract: false,
228
+ is_optional: false,
229
+ is_override: false,
230
+ readonly: false,
231
+ declare: false,
232
+ definite: false,
233
+ });
234
+
235
+ Decl::Class(ClassDecl {
236
+ ident: name,
237
+ declare: false,
238
+ class: Box::new(Class {
239
+ span: Default::default(),
240
+ ctxt: Default::default(),
241
+ decorators: vec![],
242
+ body: vec![render_prop],
243
+ super_class: Some(Box::new(Expr::Ident(super_class_ident))),
244
+ is_abstract: false,
245
+ type_params: None,
246
+ super_type_params: None,
247
+ implements: vec![],
248
+ }),
249
+ })
250
+ }
251
+
184
252
  /// Rewrite imports from "inferno" to the configured import source
185
253
  fn rewrite_inferno_imports(&mut self, module: &mut Module) {
186
254
  let import_source = self
@@ -204,12 +272,8 @@ impl RaskComponentTransform {
204
272
  }
205
273
  }
206
274
 
207
- /// Inject the RaskComponent import at the top of the module
275
+ /// Inject the RaskStatefulComponent and/or RaskStatelessComponent imports at the top of the module
208
276
  fn inject_runtime(&mut self, module: &mut Module) {
209
- if self.import_rask_component.is_none() {
210
- return;
211
- }
212
-
213
277
  let import_source = self
214
278
  .config
215
279
  .import_source
@@ -217,47 +281,93 @@ impl RaskComponentTransform {
217
281
  .map(|s| s.as_str())
218
282
  .unwrap_or("rask-ui");
219
283
 
220
- let rask_component_ident = self.import_rask_component.as_ref().unwrap();
284
+ let mut specifiers = vec![];
285
+
286
+ // Add RaskStatefulComponent if needed
287
+ if let Some(stateful_ident) = &self.import_rask_stateful_component {
288
+ // Check if import already exists
289
+ let mut exists = false;
290
+ for item in &module.body {
291
+ if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = item {
292
+ if &*import.src.value == import_source {
293
+ for spec in &import.specifiers {
294
+ if let ImportSpecifier::Named(named) = spec {
295
+ if let Some(ModuleExportName::Ident(imported)) = &named.imported {
296
+ if &*imported.sym == "RaskStatefulComponent" {
297
+ exists = true;
298
+ break;
299
+ }
300
+ } else if &*named.local.sym == "RaskStatefulComponent" {
301
+ exists = true;
302
+ break;
303
+ }
304
+ }
305
+ }
306
+ }
307
+ }
308
+ }
221
309
 
222
- // Check if import already exists
223
- for item in &module.body {
224
- if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = item {
225
- if &*import.src.value == import_source {
226
- for spec in &import.specifiers {
227
- if let ImportSpecifier::Named(named) = spec {
228
- if let Some(ModuleExportName::Ident(imported)) = &named.imported {
229
- if &*imported.sym == "RaskComponent" {
230
- return; // Import already exists
310
+ if !exists {
311
+ specifiers.push(ImportSpecifier::Named(ImportNamedSpecifier {
312
+ span: Default::default(),
313
+ local: stateful_ident.clone(),
314
+ imported: Some(ModuleExportName::Ident(quote_ident!("RaskStatefulComponent").into())),
315
+ is_type_only: false,
316
+ }));
317
+ }
318
+ }
319
+
320
+ // Add RaskStatelessComponent if needed
321
+ if let Some(stateless_ident) = &self.import_rask_stateless_component {
322
+ // Check if import already exists
323
+ let mut exists = false;
324
+ for item in &module.body {
325
+ if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = item {
326
+ if &*import.src.value == import_source {
327
+ for spec in &import.specifiers {
328
+ if let ImportSpecifier::Named(named) = spec {
329
+ if let Some(ModuleExportName::Ident(imported)) = &named.imported {
330
+ if &*imported.sym == "RaskStatelessComponent" {
331
+ exists = true;
332
+ break;
333
+ }
334
+ } else if &*named.local.sym == "RaskStatelessComponent" {
335
+ exists = true;
336
+ break;
231
337
  }
232
- } else if &*named.local.sym == "RaskComponent" {
233
- return; // Import already exists
234
338
  }
235
339
  }
236
340
  }
237
341
  }
238
342
  }
343
+
344
+ if !exists {
345
+ specifiers.push(ImportSpecifier::Named(ImportNamedSpecifier {
346
+ span: Default::default(),
347
+ local: stateless_ident.clone(),
348
+ imported: Some(ModuleExportName::Ident(quote_ident!("RaskStatelessComponent").into())),
349
+ is_type_only: false,
350
+ }));
351
+ }
239
352
  }
240
353
 
241
- // Create new import
242
- let import = ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
243
- span: Default::default(),
244
- specifiers: vec![ImportSpecifier::Named(ImportNamedSpecifier {
245
- span: Default::default(),
246
- local: rask_component_ident.clone(),
247
- imported: Some(ModuleExportName::Ident(quote_ident!("RaskComponent").into())),
248
- is_type_only: false,
249
- })],
250
- src: Box::new(Str {
354
+ // Only create import if we have specifiers to add
355
+ if !specifiers.is_empty() {
356
+ let import = ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
251
357
  span: Default::default(),
252
- value: Wtf8Atom::from(import_source),
253
- raw: None,
254
- }),
255
- type_only: false,
256
- with: None,
257
- phase: Default::default(),
258
- }));
259
-
260
- module.body.insert(0, import);
358
+ specifiers,
359
+ src: Box::new(Str {
360
+ span: Default::default(),
361
+ value: Wtf8Atom::from(import_source),
362
+ raw: None,
363
+ }),
364
+ type_only: false,
365
+ with: None,
366
+ phase: Default::default(),
367
+ }));
368
+
369
+ module.body.insert(0, import);
370
+ }
261
371
  }
262
372
  }
263
373
 
@@ -278,11 +388,19 @@ impl VisitMut for RaskComponentTransform {
278
388
  fn visit_mut_module_item(&mut self, item: &mut ModuleItem) {
279
389
  match item {
280
390
  ModuleItem::Stmt(Stmt::Decl(Decl::Fn(fn_decl))) => {
281
- // Check if this function should be transformed
391
+ // Check for stateful component first (returns arrow function)
282
392
  if self.is_rask_component(&fn_decl.function) {
283
393
  let name = fn_decl.ident.clone();
284
394
  let func = (*fn_decl.function).clone();
285
- let class_decl = self.transform_to_class(name, func);
395
+ let class_decl = self.transform_to_stateful_class(name, func);
396
+ *item = ModuleItem::Stmt(Stmt::Decl(class_decl));
397
+ return;
398
+ }
399
+ // Then check for stateless component (directly returns VNode)
400
+ else if self.is_stateless_component(&fn_decl.function) {
401
+ let name = fn_decl.ident.clone();
402
+ let func = (*fn_decl.function).clone();
403
+ let class_decl = self.transform_to_stateless_class(name, func);
286
404
  *item = ModuleItem::Stmt(Stmt::Decl(class_decl));
287
405
  return;
288
406
  }
@@ -295,16 +413,27 @@ impl VisitMut for RaskComponentTransform {
295
413
  // but transform the pattern internally if needed
296
414
  // For now, we'll skip transforming default exports
297
415
  // as they need special handling
416
+ } else if self.is_stateless_component(&fn_expr.function) {
417
+ // Same for stateless default exports
298
418
  }
299
419
  }
300
420
  }
301
421
  ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export)) => {
302
422
  // Handle: export function MyComponent() { return () => <div /> }
303
423
  if let Decl::Fn(fn_decl) = &mut export.decl {
424
+ // Check for stateful component first
304
425
  if self.is_rask_component(&fn_decl.function) {
305
426
  let name = fn_decl.ident.clone();
306
427
  let func = (*fn_decl.function).clone();
307
- let class_decl = self.transform_to_class(name, func);
428
+ let class_decl = self.transform_to_stateful_class(name, func);
429
+ export.decl = class_decl;
430
+ return;
431
+ }
432
+ // Then check for stateless component
433
+ else if self.is_stateless_component(&fn_decl.function) {
434
+ let name = fn_decl.ident.clone();
435
+ let func = (*fn_decl.function).clone();
436
+ let class_decl = self.transform_to_stateless_class(name, func);
308
437
  export.decl = class_decl;
309
438
  return;
310
439
  }