Skip to content

Commit 7690c42

Browse files
authored
Merge pull request #3 from easyops-cn/steve/event-callback
feat(): support event callback declaration
2 parents 2dc5de9 + d6a6a09 commit 7690c42

File tree

8 files changed

+68
-27
lines changed

8 files changed

+68
-27
lines changed

apps/test/src/Pages/Layout.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,14 @@ export default function Layout() {
2828
age: 30,
2929
};
3030

31+
const handleClick = () => {
32+
setA("New Value by callback");
33+
setC((prev) => prev + 42);
34+
};
35+
3136
return (
3237
<>
33-
<button
34-
onClick={() => {
35-
setA("New Value");
36-
setC((prev) => prev + 42);
37-
}}
38-
>
39-
My App
40-
</button>
38+
<button onClick={handleClick}>My App</button>
4139
<pre>{JSON.stringify(sessionStore.getItem("myKey"), null, 2)}</pre>
4240
<eo-descriptions
4341
dataSource={dataSource}

packages/parser/src/__snapshots__/parseTemplate.spec.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ exports[`parseTemplate should work 1`] = `
141141
"start": 81,
142142
"type": "Identifier",
143143
},
144-
"kind": "eventHandler",
144+
"kind": "eventHandlerParam",
145145
},
146146
Node {
147147
"end": 105,
@@ -185,7 +185,7 @@ exports[`parseTemplate should work 1`] = `
185185
"start": 93,
186186
"type": "Identifier",
187187
},
188-
"kind": "eventHandler",
188+
"kind": "eventHandlerParam",
189189
},
190190
},
191191
"children": [

packages/parser/src/__snapshots__/parseView.spec.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,7 +1116,7 @@ exports[`parseView should work 1`] = `
11161116
"start": 218,
11171117
"type": "Identifier",
11181118
},
1119-
"kind": "eventHandler",
1119+
"kind": "eventHandlerParam",
11201120
},
11211121
Node {
11221122
"end": 242,
@@ -1160,7 +1160,7 @@ exports[`parseView should work 1`] = `
11601160
"start": 230,
11611161
"type": "Identifier",
11621162
},
1163-
"kind": "eventHandler",
1163+
"kind": "eventHandlerParam",
11641164
},
11651165
},
11661166
"children": [

packages/parser/src/modules/interfaces.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
LifeCycle,
1111
ParseError,
1212
} from "../interfaces.js";
13+
import type { NodePath } from "@babel/traverse";
1314

1415
export interface ParsedApp {
1516
appType: "app" | "view" | "template";
@@ -100,7 +101,8 @@ export interface BindingInfo {
100101
| "flags"
101102
| "media"
102103
| "param"
103-
| "eventHandler"
104+
| "eventHandlerParam"
105+
| "eventCallback"
104106
| "component"
105107
| "context"
106108
| "function";
@@ -117,6 +119,9 @@ export interface BindingInfo {
117119
/** For kind "context" */
118120
contextProvider?: ContextReference;
119121
contextKey?: string;
122+
123+
/** For kind "eventCallback" */
124+
callback?: NodePath<t.FunctionExpression | t.ArrowFunctionExpression>;
120125
}
121126

122127
export interface EventBindingInfo {

packages/parser/src/modules/parseComponent.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
EffectsMap,
1212
} from "./interfaces.js";
1313
import {
14+
isGeneralFunctionExpression,
1415
validateEmbeddedExpression,
1516
validateFunction,
1617
validateGlobalApi,
@@ -118,7 +119,10 @@ export function parseComponent(
118119
});
119120
return null;
120121
}
121-
bindingMap.set(value.node, { id: value.node, kind: "eventHandler" });
122+
bindingMap.set(value.node, {
123+
id: value.node,
124+
kind: "eventHandlerParam",
125+
});
122126
} else {
123127
let bindingId: t.Identifier | undefined;
124128
let initialValue: unknown;
@@ -312,6 +316,20 @@ export function parseComponent(
312316
continue;
313317
}
314318

319+
if (isGeneralFunctionExpression(init)) {
320+
const callbackBody = init.get("body");
321+
if (callbackBody.isBlockStatement()) {
322+
// It's a event callback function
323+
const funcBinding: BindingInfo = {
324+
id: declId.node,
325+
kind: "eventCallback",
326+
callback: init,
327+
};
328+
bindingMap.set(declId.node, funcBinding);
329+
continue;
330+
}
331+
}
332+
315333
const binding: BindingInfo = { id: declId.node, kind: "constant" };
316334
bindingMap.set(declId.node, binding);
317335
if (init.node) {

packages/parser/src/modules/parseEvent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ export function parseEventHandler(
234234
],
235235
},
236236
};
237-
case "eventHandler": {
237+
case "eventHandlerParam": {
238238
if (args.length > 1) {
239239
state.errors.push({
240240
message: `Event dispatcher "${callee.node.name}" expects at most 1 argument, but got ${args.length}`,

packages/parser/src/modules/parseJSXElement.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -209,28 +209,36 @@ export function parseJSXElement(
209209
continue;
210210
}
211211
let handler: EventHandler[] | null = null;
212+
let eventPath = exprPath;
212213
// Assert: exprPath.isReferencedIdentifier()
213214
if (exprPath.isIdentifier()) {
214215
const bindingId = exprPath.scope.getBindingIdentifier(
215216
exprPath.node.name
216217
);
217218
if (bindingId) {
218219
const binding = options.component?.bindingMap.get(bindingId);
219-
if (binding && binding.kind === "eventHandler") {
220-
handler = [
221-
{
222-
action: "dispatch_event",
223-
payload: {
224-
type: convertJsxEventAttr(binding.id.name),
225-
detail: "<% EVENT.detail %>",
226-
},
227-
},
228-
];
220+
if (binding) {
221+
switch (binding.kind) {
222+
case "eventHandlerParam":
223+
handler = [
224+
{
225+
action: "dispatch_event",
226+
payload: {
227+
type: convertJsxEventAttr(binding.id.name),
228+
detail: "<% EVENT.detail %>",
229+
},
230+
},
231+
];
232+
break;
233+
case "eventCallback":
234+
eventPath = binding.callback!;
235+
break;
236+
}
229237
}
230238
}
231239
}
232240
if (!handler) {
233-
handler = parseEvent(exprPath, state, app, options);
241+
handler = parseEvent(eventPath, state, app, options);
234242
}
235243
if (handler) {
236244
if (attrName === "onMount" || attrName === "onUnmount") {

packages/parser/src/modules/parseJsValue.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,19 @@ export function parseJsValue(
6262
return null;
6363
}
6464
const paramNames: string[] = [];
65-
for (const param of path.get("params")) {
65+
66+
const params = path.get("params");
67+
if (params.length > 1 && options.component?.id?.name.includes("-")) {
68+
state.errors.push({
69+
message:
70+
"Multiple parameters in render callback are not supported in custom elements",
71+
node: params[1].node,
72+
severity: "error",
73+
});
74+
return null;
75+
}
76+
77+
for (const param of params) {
6678
if (param.isIdentifier()) {
6779
paramNames.push(param.node.name);
6880
} else {

0 commit comments

Comments
 (0)