-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathmapping2ForPlugin.ts
More file actions
123 lines (114 loc) · 3.82 KB
/
mapping2ForPlugin.ts
File metadata and controls
123 lines (114 loc) · 3.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import babel, { NodePath, PluginObj } from '@babel/core';
import { register, types as t } from '@openinula/babel-api';
/**
* Mapping to for Plugin
* Convert map in JSXExpressionContainer to for
* arr.map((item) => (<div>{item}</div>)) -> <for each={arr}>{item => <div>{item}</div>}</for>
* Convert last map of multiple map call to for element
* arr.map(item => <h1>{item}</h1>).map((item) => (<div>{item}</div>)) -> <for each={arr.map(item => <h1>{item}</h1>)}>{item => <div>{item}</div>}</for>
* Convert map of map to for in for
* <for each={matrix}>{arr => <for each={arr}>{item => <div>{item}</div>}</for>}</for>
*
* @param api Babel api
* @return PluginObj mapping to for plugin
*/
export default function (api: typeof babel): PluginObj {
register(api);
return {
visitor: {
Program(program) {
program.traverse({
CallExpression(path: NodePath<t.CallExpression>) {
mapCallVisitor(path, false);
},
OptionalCallExpression(path: NodePath<t.OptionalCallExpression>) {
mapCallVisitor(path, false);
},
});
},
},
};
}
/**
* Convert map call (regular or optional) in JSXExpressionContainer to for visitor
*
* @param path Map call expression path (CallExpression or OptionalCallExpression)
* @param inner is inside for tag
*/
function mapCallVisitor(path: NodePath<t.CallExpression | t.OptionalCallExpression>, inner: boolean): void {
//match arrow function map call inside for tag
if (inner && !path.parentPath.isArrowFunctionExpression()) {
return;
}
//match map call in jsx expression container
if (!inner && !path.parentPath.isJSXExpressionContainer()) {
return;
}
// don't convert map call inside for tag
if (path.parentPath?.parentPath?.parentPath?.isJSXOpeningElement()) {
return;
}
const callee = path.get('callee');
if (!callee.isMemberExpression() && !callee.isOptionalMemberExpression()) {
return;
}
// Handle both MemberExpression and OptionalMemberExpression
const objectProperty = callee.get('object');
const object = Array.isArray(objectProperty) ? objectProperty[0] : objectProperty;
const mapProperty = callee.get('property');
const map = Array.isArray(mapProperty) ? mapProperty[0] : mapProperty;
if (!map.isIdentifier()) {
return;
}
if (map.node.name !== 'map') {
return;
}
const mapArgs = path.get('arguments');
if (mapArgs.length !== 1) {
return;
}
const expression = mapArgs[0];
if (!expression.isExpression()) {
return;
}
// generate for tag
const forElement = t.jsxElement(
t.jsxOpeningElement(
t.jSXIdentifier('for'),
[t.jsxAttribute(t.jsxIdentifier('each'), t.jsxExpressionContainer(object.node))],
false
),
t.jsxClosingElement(t.jSXIdentifier('for')),
[t.jSXExpressionContainer(expression.node)]
);
if (path.parentPath.isArrowFunctionExpression()) {
path.replaceWith(forElement);
} else {
path.parentPath.replaceWith(forElement);
}
// convert map call of arrow function inside for tag
if (!inner) {
path.parentPath.traverse({
CallExpression(path: NodePath<t.CallExpression>) {
mapCallVisitor(path, true);
},
OptionalCallExpression(path: NodePath<t.OptionalCallExpression>) {
mapCallVisitor(path, true);
},
});
}
}