@base44-preview/vite-plugin 0.2.25-pr.36.792e85b → 0.2.26-pr.43.056eecd
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/dist/consts.d.ts +13 -0
- package/dist/consts.d.ts.map +1 -0
- package/dist/consts.js +13 -0
- package/dist/consts.js.map +1 -0
- package/dist/jsx-processor.d.ts +10 -1
- package/dist/jsx-processor.d.ts.map +1 -1
- package/dist/jsx-processor.js +47 -5
- package/dist/jsx-processor.js.map +1 -1
- package/dist/jsx-utils.d.ts +9 -0
- package/dist/jsx-utils.d.ts.map +1 -1
- package/dist/jsx-utils.js +86 -0
- package/dist/jsx-utils.js.map +1 -1
- package/dist/processors/collection-id-processor.d.ts +20 -0
- package/dist/processors/collection-id-processor.d.ts.map +1 -0
- package/dist/processors/collection-id-processor.js +182 -0
- package/dist/processors/collection-id-processor.js.map +1 -0
- package/dist/processors/collection-item-field-processor.d.ts +22 -0
- package/dist/processors/collection-item-field-processor.d.ts.map +1 -0
- package/dist/processors/collection-item-field-processor.js +215 -0
- package/dist/processors/collection-item-field-processor.js.map +1 -0
- package/dist/processors/collection-item-id-processor.d.ts +12 -0
- package/dist/processors/collection-item-id-processor.d.ts.map +1 -0
- package/dist/processors/collection-item-id-processor.js +50 -0
- package/dist/processors/collection-item-id-processor.js.map +1 -0
- package/dist/processors/collection-reference-field-processor.d.ts +31 -0
- package/dist/processors/collection-reference-field-processor.d.ts.map +1 -0
- package/dist/processors/collection-reference-field-processor.js +174 -0
- package/dist/processors/collection-reference-field-processor.js.map +1 -0
- package/dist/processors/collection-tracing-utils.d.ts +31 -0
- package/dist/processors/collection-tracing-utils.d.ts.map +1 -0
- package/dist/processors/collection-tracing-utils.js +326 -0
- package/dist/processors/collection-tracing-utils.js.map +1 -0
- package/dist/processors/shared-utils.d.ts +64 -0
- package/dist/processors/shared-utils.d.ts.map +1 -1
- package/dist/processors/shared-utils.js +464 -0
- package/dist/processors/shared-utils.js.map +1 -1
- package/dist/processors/static-array-processor.d.ts +2 -3
- package/dist/processors/static-array-processor.d.ts.map +1 -1
- package/dist/processors/static-array-processor.js +2 -3
- package/dist/processors/static-array-processor.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/visual-edit-plugin.d.ts +0 -1
- package/dist/visual-edit-plugin.d.ts.map +1 -1
- package/dist/visual-edit-plugin.js +29 -178
- package/dist/visual-edit-plugin.js.map +1 -1
- package/package.json +1 -1
- package/src/consts.ts +14 -0
- package/src/jsx-processor.ts +58 -9
- package/src/jsx-utils.ts +116 -0
- package/src/processors/collection-id-processor.ts +261 -0
- package/src/processors/collection-item-field-processor.ts +321 -0
- package/src/processors/collection-item-id-processor.ts +69 -0
- package/src/processors/collection-reference-field-processor.ts +225 -0
- package/src/processors/collection-tracing-utils.ts +436 -0
- package/src/processors/shared-utils.ts +595 -0
- package/src/processors/static-array-processor.ts +6 -3
- package/src/types.ts +4 -0
- package/src/visual-edit-plugin.ts +34 -215
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import type { NodePath } from "@babel/traverse";
|
|
2
|
+
import type * as t from "@babel/types";
|
|
3
|
+
import {
|
|
4
|
+
DATA_COLLECTION_FIELD_REFERENCE,
|
|
5
|
+
DATA_COLLECTION_REFERENCE,
|
|
6
|
+
} from "../consts.js";
|
|
7
|
+
import {
|
|
8
|
+
JSXAttributeUtils,
|
|
9
|
+
ExpressionAnalysisUtils,
|
|
10
|
+
TypeCheckUtils,
|
|
11
|
+
} from "./shared-utils.js";
|
|
12
|
+
|
|
13
|
+
export class ReferenceFieldProcessor {
|
|
14
|
+
private attributeUtils: JSXAttributeUtils;
|
|
15
|
+
private expressionUtils: ExpressionAnalysisUtils;
|
|
16
|
+
private typeCheckUtils: TypeCheckUtils;
|
|
17
|
+
|
|
18
|
+
constructor(private types: typeof t) {
|
|
19
|
+
this.attributeUtils = new JSXAttributeUtils(types);
|
|
20
|
+
this.expressionUtils = new ExpressionAnalysisUtils(types);
|
|
21
|
+
this.typeCheckUtils = new TypeCheckUtils(types);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
process(path: NodePath<t.JSXOpeningElement>): void {
|
|
25
|
+
if (
|
|
26
|
+
this.attributeUtils.hasAttribute(path, DATA_COLLECTION_FIELD_REFERENCE)
|
|
27
|
+
) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
this.isReturnedFromReferenceMap(path) ||
|
|
33
|
+
this.isInsideReferenceLogicalExpression(path) ||
|
|
34
|
+
this.isInReferencePropertyMapChain(path)
|
|
35
|
+
) {
|
|
36
|
+
this.attributeUtils.addStringAttribute(
|
|
37
|
+
path,
|
|
38
|
+
DATA_COLLECTION_FIELD_REFERENCE,
|
|
39
|
+
"true"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Strategy A: Element is the direct return of a .map() callback
|
|
46
|
+
* whose array is a reference variable (initialized via type-check pattern).
|
|
47
|
+
*/
|
|
48
|
+
private isReturnedFromReferenceMap(
|
|
49
|
+
path: NodePath<t.JSXOpeningElement>
|
|
50
|
+
): boolean {
|
|
51
|
+
const jsxElement = path.parentPath;
|
|
52
|
+
if (!jsxElement?.isJSXElement()) return false;
|
|
53
|
+
|
|
54
|
+
const parent = jsxElement.parentPath;
|
|
55
|
+
if (!parent) return false;
|
|
56
|
+
|
|
57
|
+
const callbackFn = this.findEnclosingMapCallback(parent);
|
|
58
|
+
if (!callbackFn) return false;
|
|
59
|
+
|
|
60
|
+
const mapCall = callbackFn.parentPath;
|
|
61
|
+
if (!mapCall?.isCallExpression()) return false;
|
|
62
|
+
|
|
63
|
+
const callee = mapCall.get("callee");
|
|
64
|
+
if (!callee.isMemberExpression()) return false;
|
|
65
|
+
|
|
66
|
+
const arrayObj = callee.get("object") as NodePath<t.Expression>;
|
|
67
|
+
return this.isReferenceArray(arrayObj);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Strategy B: Element is inside a && expression where the left operand
|
|
72
|
+
* references a field name that appears in an ancestor's data-collection-reference.
|
|
73
|
+
*/
|
|
74
|
+
private isInsideReferenceLogicalExpression(
|
|
75
|
+
path: NodePath<t.JSXOpeningElement>
|
|
76
|
+
): boolean {
|
|
77
|
+
const jsxElement = path.parentPath;
|
|
78
|
+
if (!jsxElement?.isJSXElement()) return false;
|
|
79
|
+
|
|
80
|
+
const logicalParent = jsxElement.parentPath;
|
|
81
|
+
if (!logicalParent?.isLogicalExpression()) return false;
|
|
82
|
+
if (logicalParent.node.operator !== "&&") return false;
|
|
83
|
+
|
|
84
|
+
const left = logicalParent.node.left;
|
|
85
|
+
const fieldName = this.extractFieldNameFromExpression(left as t.Expression);
|
|
86
|
+
if (!fieldName) return false;
|
|
87
|
+
|
|
88
|
+
return this.isFieldInAncestorReference(path, fieldName);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Strategy C: Element is inside a .map() chain on a reference property.
|
|
93
|
+
* e.g. item.relatedProducts.map(...) or item.relatedProducts.filter(...).map(...)
|
|
94
|
+
*/
|
|
95
|
+
private isInReferencePropertyMapChain(
|
|
96
|
+
path: NodePath<t.JSXOpeningElement>
|
|
97
|
+
): boolean {
|
|
98
|
+
const jsxElement = path.parentPath;
|
|
99
|
+
if (!jsxElement?.isJSXElement()) return false;
|
|
100
|
+
|
|
101
|
+
let current: NodePath | null = jsxElement;
|
|
102
|
+
while (current) {
|
|
103
|
+
if (current.isCallExpression()) {
|
|
104
|
+
const callee = current.get("callee");
|
|
105
|
+
if (callee.isMemberExpression()) {
|
|
106
|
+
const fieldName = this.extractReferenceFieldFromMapChain(callee);
|
|
107
|
+
if (fieldName && this.isFieldInAncestorReference(path, fieldName)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
current = current.parentPath;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private findEnclosingMapCallback(
|
|
119
|
+
path: NodePath
|
|
120
|
+
): NodePath<t.Function> | null {
|
|
121
|
+
let current: NodePath | null = path;
|
|
122
|
+
while (current) {
|
|
123
|
+
if (current.isArrowFunctionExpression() || current.isFunctionExpression()) {
|
|
124
|
+
const parent = current.parentPath;
|
|
125
|
+
if (parent?.isCallExpression()) {
|
|
126
|
+
const callee = parent.get("callee");
|
|
127
|
+
if (callee.isMemberExpression()) {
|
|
128
|
+
const prop = callee.get("property") as NodePath;
|
|
129
|
+
if (
|
|
130
|
+
prop.isIdentifier() &&
|
|
131
|
+
(prop.node.name === "map" || prop.node.name === "flatMap")
|
|
132
|
+
) {
|
|
133
|
+
return current as NodePath<t.Function>;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
current = current.parentPath;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private isReferenceArray(path: NodePath<t.Expression>): boolean {
|
|
144
|
+
if (!path.isIdentifier()) return false;
|
|
145
|
+
|
|
146
|
+
const binding = path.scope.getBinding(path.node.name);
|
|
147
|
+
if (!binding?.path.isVariableDeclarator()) return false;
|
|
148
|
+
|
|
149
|
+
const init = binding.path.get("init");
|
|
150
|
+
if (!init.isConditionalExpression()) return false;
|
|
151
|
+
|
|
152
|
+
const test = init.node.test;
|
|
153
|
+
return this.typeCheckUtils.isReferenceTypeCheck(test as t.Expression);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private extractFieldNameFromExpression(
|
|
157
|
+
node: t.Expression
|
|
158
|
+
): string | null {
|
|
159
|
+
if (this.types.isMemberExpression(node)) {
|
|
160
|
+
const prop = node.property;
|
|
161
|
+
if (this.types.isIdentifier(prop)) return prop.name;
|
|
162
|
+
}
|
|
163
|
+
if (this.types.isOptionalMemberExpression(node)) {
|
|
164
|
+
const prop = node.property;
|
|
165
|
+
if (this.types.isIdentifier(prop)) return prop.name;
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private isFieldInAncestorReference(
|
|
171
|
+
path: NodePath,
|
|
172
|
+
fieldName: string
|
|
173
|
+
): boolean {
|
|
174
|
+
const ancestor = this.attributeUtils.findAncestorWithAttribute(
|
|
175
|
+
path,
|
|
176
|
+
DATA_COLLECTION_REFERENCE
|
|
177
|
+
);
|
|
178
|
+
if (!ancestor) return false;
|
|
179
|
+
|
|
180
|
+
const refValue = this.attributeUtils.getAttributeStringValue(
|
|
181
|
+
ancestor,
|
|
182
|
+
DATA_COLLECTION_REFERENCE
|
|
183
|
+
);
|
|
184
|
+
if (!refValue) return false;
|
|
185
|
+
|
|
186
|
+
const references = refValue.split(",").map((r) => r.trim());
|
|
187
|
+
return references.includes(fieldName);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private extractReferenceFieldFromMapChain(
|
|
191
|
+
callee: NodePath<t.MemberExpression>
|
|
192
|
+
): string | null {
|
|
193
|
+
const prop = callee.get("property") as NodePath;
|
|
194
|
+
if (!prop.isIdentifier()) return null;
|
|
195
|
+
|
|
196
|
+
const methodName = prop.node.name;
|
|
197
|
+
const obj = callee.get("object") as NodePath<t.Expression>;
|
|
198
|
+
|
|
199
|
+
if (methodName === "map" || methodName === "flatMap") {
|
|
200
|
+
if (obj.isMemberExpression()) {
|
|
201
|
+
const fieldProp = obj.get("property") as NodePath;
|
|
202
|
+
if (fieldProp.isIdentifier()) return fieldProp.node.name;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (methodName === "map" || methodName === "flatMap") {
|
|
207
|
+
if (obj.isCallExpression()) {
|
|
208
|
+
const innerCallee = obj.get("callee");
|
|
209
|
+
if (innerCallee.isMemberExpression()) {
|
|
210
|
+
return this.extractReferenceFieldFromMapChain(innerCallee);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (
|
|
216
|
+
["filter", "sort", "slice"].includes(methodName) &&
|
|
217
|
+
obj.isMemberExpression()
|
|
218
|
+
) {
|
|
219
|
+
const fieldProp = obj.get("property") as NodePath;
|
|
220
|
+
if (fieldProp.isIdentifier()) return fieldProp.node.name;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import type { NodePath } from "@babel/traverse";
|
|
2
|
+
import type * as t from "@babel/types";
|
|
3
|
+
import type { CollectionInfo } from "../types.js";
|
|
4
|
+
import {
|
|
5
|
+
ExpressionAnalysisUtils,
|
|
6
|
+
BindingUtils,
|
|
7
|
+
CallExpressionUtils,
|
|
8
|
+
} from "./shared-utils.js";
|
|
9
|
+
|
|
10
|
+
export class CollectionTracingUtils {
|
|
11
|
+
private expressionUtils: ExpressionAnalysisUtils;
|
|
12
|
+
private bindingUtils: BindingUtils;
|
|
13
|
+
private callUtils: CallExpressionUtils;
|
|
14
|
+
private visitedSetters = new Set<string>();
|
|
15
|
+
private visitedGetByIdSetters = new Set<string>();
|
|
16
|
+
|
|
17
|
+
constructor(private types: typeof t) {
|
|
18
|
+
this.expressionUtils = new ExpressionAnalysisUtils(types);
|
|
19
|
+
this.bindingUtils = new BindingUtils(types);
|
|
20
|
+
this.callUtils = new CallExpressionUtils(types);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
resetVisited(): void {
|
|
24
|
+
this.visitedSetters.clear();
|
|
25
|
+
this.visitedGetByIdSetters.clear();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
traceCollectionSource(
|
|
29
|
+
path: NodePath<t.Expression>
|
|
30
|
+
): CollectionInfo | null {
|
|
31
|
+
this.resetVisited();
|
|
32
|
+
return this.traceExpression(path);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private traceExpression(
|
|
36
|
+
path: NodePath<t.Expression>
|
|
37
|
+
): CollectionInfo | null {
|
|
38
|
+
if (path.isIdentifier()) {
|
|
39
|
+
return this.traceIdentifierToCollection(path);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (path.isMemberExpression()) {
|
|
43
|
+
return this.traceMemberExpression(path);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (path.isCallExpression()) {
|
|
47
|
+
return this.traceCallExpression(path);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (path.isAwaitExpression()) {
|
|
51
|
+
const argument = path.get("argument");
|
|
52
|
+
if (argument.isExpression()) {
|
|
53
|
+
return this.traceExpression(argument);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (path.isArrayExpression()) {
|
|
58
|
+
return this.traceArrayExpression(path);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private traceIdentifierToCollection(
|
|
65
|
+
path: NodePath<t.Identifier>
|
|
66
|
+
): CollectionInfo | null {
|
|
67
|
+
const name = path.node.name;
|
|
68
|
+
const binding = path.scope.getBinding(name);
|
|
69
|
+
if (!binding) return null;
|
|
70
|
+
|
|
71
|
+
if (binding.path.isVariableDeclarator()) {
|
|
72
|
+
return this.traceVariableDeclarator(binding.path, name);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (this.bindingUtils.isFunctionParameter(name, path)) {
|
|
76
|
+
return this.traceParameterToCollection(name, path);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private traceVariableDeclarator(
|
|
83
|
+
declaratorPath: NodePath<t.VariableDeclarator>,
|
|
84
|
+
variableName: string
|
|
85
|
+
): CollectionInfo | null {
|
|
86
|
+
const init = declaratorPath.get("init");
|
|
87
|
+
|
|
88
|
+
if (init.isCallExpression()) {
|
|
89
|
+
const directResult = this.checkDirectServiceCall(init);
|
|
90
|
+
if (directResult) return directResult;
|
|
91
|
+
|
|
92
|
+
const useStateInfo = this.bindingUtils.isUseStateCall(init);
|
|
93
|
+
if (useStateInfo?.setterName) {
|
|
94
|
+
return this.traceUseStateSetter(useStateInfo.setterName, declaratorPath);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (init.isAwaitExpression()) {
|
|
99
|
+
const awaitArg = init.get("argument");
|
|
100
|
+
if (awaitArg.isCallExpression()) {
|
|
101
|
+
const directResult = this.checkDirectServiceCall(awaitArg);
|
|
102
|
+
if (directResult) return directResult;
|
|
103
|
+
}
|
|
104
|
+
return this.traceExpression(init);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (init.isMemberExpression()) {
|
|
108
|
+
return this.traceExpression(init);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (init.isIdentifier()) {
|
|
112
|
+
return this.traceExpression(init);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (init.isCallExpression()) {
|
|
116
|
+
return this.traceExpression(init);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const id = declaratorPath.get("id");
|
|
120
|
+
|
|
121
|
+
if (id.isArrayPattern()) {
|
|
122
|
+
return this.traceArrayDestructuring(declaratorPath, id, variableName);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (id.isObjectPattern()) {
|
|
126
|
+
return this.traceObjectDestructuring(declaratorPath, id, variableName);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private traceUseStateSetter(
|
|
133
|
+
setterName: string,
|
|
134
|
+
declaratorPath: NodePath<t.VariableDeclarator>
|
|
135
|
+
): CollectionInfo | null {
|
|
136
|
+
if (this.visitedSetters.has(setterName)) return null;
|
|
137
|
+
this.visitedSetters.add(setterName);
|
|
138
|
+
|
|
139
|
+
const functionScope = declaratorPath.getFunctionParent() ?? declaratorPath.scope.path;
|
|
140
|
+
const setterCall = this.bindingUtils.findSetterCallInScope(
|
|
141
|
+
setterName,
|
|
142
|
+
functionScope
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
if (!setterCall) return null;
|
|
146
|
+
|
|
147
|
+
const args = setterCall.get("arguments");
|
|
148
|
+
const firstArg = args[0];
|
|
149
|
+
if (!firstArg || !firstArg.isExpression()) return null;
|
|
150
|
+
|
|
151
|
+
return this.traceExpression(firstArg);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private traceMemberExpression(
|
|
155
|
+
path: NodePath<t.MemberExpression>
|
|
156
|
+
): CollectionInfo | null {
|
|
157
|
+
const property = path.get("property");
|
|
158
|
+
const object = path.get("object");
|
|
159
|
+
|
|
160
|
+
if (
|
|
161
|
+
property.isIdentifier() &&
|
|
162
|
+
property.node.name === "items"
|
|
163
|
+
) {
|
|
164
|
+
if (object.isExpression()) {
|
|
165
|
+
return this.traceExpression(object);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (object.isIdentifier()) {
|
|
170
|
+
const objResult = this.traceIdentifierToCollection(object);
|
|
171
|
+
if (objResult && property.isIdentifier()) {
|
|
172
|
+
if (objResult.references.includes(property.node.name)) {
|
|
173
|
+
return { id: property.node.name, references: [] };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return objResult;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (object.isExpression()) {
|
|
180
|
+
return this.traceExpression(object);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private traceCallExpression(
|
|
187
|
+
path: NodePath<t.CallExpression>
|
|
188
|
+
): CollectionInfo | null {
|
|
189
|
+
const directResult = this.checkDirectServiceCall(path);
|
|
190
|
+
if (directResult) return directResult;
|
|
191
|
+
|
|
192
|
+
if (this.callUtils.isChainedArrayMethod(path.node)) {
|
|
193
|
+
const callee = path.get("callee");
|
|
194
|
+
if (callee.isMemberExpression()) {
|
|
195
|
+
const obj = callee.get("object") as NodePath<t.Expression>;
|
|
196
|
+
return this.traceExpression(obj);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private checkDirectServiceCall(
|
|
204
|
+
path: NodePath<t.CallExpression>
|
|
205
|
+
): CollectionInfo | null {
|
|
206
|
+
const getAllInfo = this.callUtils.isGetAllCall(path.node);
|
|
207
|
+
if (getAllInfo) {
|
|
208
|
+
return {
|
|
209
|
+
id: getAllInfo.collectionName,
|
|
210
|
+
references: getAllInfo.references,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const getByIdInfo = this.callUtils.isGetByIdCall(path.node);
|
|
215
|
+
if (getByIdInfo) {
|
|
216
|
+
return {
|
|
217
|
+
id: getByIdInfo.collectionName,
|
|
218
|
+
references: getByIdInfo.multiRefFields,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private traceArrayDestructuring(
|
|
226
|
+
declaratorPath: NodePath<t.VariableDeclarator>,
|
|
227
|
+
pattern: NodePath<t.ArrayPattern>,
|
|
228
|
+
variableName: string
|
|
229
|
+
): CollectionInfo | null {
|
|
230
|
+
const elements = pattern.get("elements");
|
|
231
|
+
const index = elements.findIndex(
|
|
232
|
+
(el) => el.isIdentifier() && el.node.name === variableName
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
if (index === -1) return null;
|
|
236
|
+
|
|
237
|
+
const init = declaratorPath.get("init");
|
|
238
|
+
|
|
239
|
+
if (init.hasNode() && this.bindingUtils.isPromiseAllCall(init as NodePath<t.Node>)) {
|
|
240
|
+
return this.tracePromiseAllElement(init as NodePath<t.Node>, index);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (init.isCallExpression()) {
|
|
244
|
+
const useStateInfo = this.bindingUtils.isUseStateCall(init);
|
|
245
|
+
if (useStateInfo && index === 0 && useStateInfo.setterName) {
|
|
246
|
+
return this.traceUseStateSetter(useStateInfo.setterName, declaratorPath);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private traceObjectDestructuring(
|
|
254
|
+
declaratorPath: NodePath<t.VariableDeclarator>,
|
|
255
|
+
pattern: NodePath<t.ObjectPattern>,
|
|
256
|
+
variableName: string
|
|
257
|
+
): CollectionInfo | null {
|
|
258
|
+
const init = declaratorPath.get("init");
|
|
259
|
+
if (!init.isExpression()) return null;
|
|
260
|
+
|
|
261
|
+
const prop = pattern.node.properties.find(
|
|
262
|
+
(p) =>
|
|
263
|
+
this.types.isObjectProperty(p) &&
|
|
264
|
+
this.types.isIdentifier(p.value) &&
|
|
265
|
+
p.value.name === variableName
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
if (!prop || !this.types.isObjectProperty(prop)) return null;
|
|
269
|
+
|
|
270
|
+
const key = prop.key;
|
|
271
|
+
const propertyName = this.types.isIdentifier(key)
|
|
272
|
+
? key.name
|
|
273
|
+
: this.types.isStringLiteral(key)
|
|
274
|
+
? key.value
|
|
275
|
+
: null;
|
|
276
|
+
|
|
277
|
+
if (propertyName === "items") {
|
|
278
|
+
return this.traceExpression(init);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return this.traceExpression(init);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private tracePromiseAllElement(
|
|
285
|
+
init: NodePath<t.Node>,
|
|
286
|
+
index: number
|
|
287
|
+
): CollectionInfo | null {
|
|
288
|
+
let callExpr: NodePath<t.CallExpression>;
|
|
289
|
+
|
|
290
|
+
if (init.isAwaitExpression()) {
|
|
291
|
+
const arg = init.get("argument");
|
|
292
|
+
if (!arg.isCallExpression()) return null;
|
|
293
|
+
callExpr = arg;
|
|
294
|
+
} else if (init.isCallExpression()) {
|
|
295
|
+
callExpr = init;
|
|
296
|
+
} else {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const args = callExpr.get("arguments");
|
|
301
|
+
const firstArg = args[0];
|
|
302
|
+
if (!firstArg?.isArrayExpression()) return null;
|
|
303
|
+
|
|
304
|
+
const elements = firstArg.get("elements");
|
|
305
|
+
const targetElement = elements[index];
|
|
306
|
+
if (!targetElement || !targetElement.isExpression()) return null;
|
|
307
|
+
|
|
308
|
+
return this.traceExpression(targetElement);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private traceParameterToCollection(
|
|
312
|
+
paramName: string,
|
|
313
|
+
path: NodePath
|
|
314
|
+
): CollectionInfo | null {
|
|
315
|
+
const fn = path.getFunctionParent();
|
|
316
|
+
if (!fn) return null;
|
|
317
|
+
|
|
318
|
+
const fnName = this.getFunctionName(fn);
|
|
319
|
+
if (!fnName) return null;
|
|
320
|
+
|
|
321
|
+
const programPath = fn.findParent((p) => p.isProgram());
|
|
322
|
+
if (!programPath) return null;
|
|
323
|
+
|
|
324
|
+
let result: CollectionInfo | null = null;
|
|
325
|
+
|
|
326
|
+
programPath.traverse({
|
|
327
|
+
JSXOpeningElement: (jsxPath: NodePath<t.JSXOpeningElement>) => {
|
|
328
|
+
if (result) return;
|
|
329
|
+
|
|
330
|
+
const elementName = this.getJSXElementName(jsxPath.node);
|
|
331
|
+
if (elementName !== fnName) return;
|
|
332
|
+
|
|
333
|
+
for (const attr of jsxPath.node.attributes) {
|
|
334
|
+
if (!this.types.isJSXAttribute(attr)) continue;
|
|
335
|
+
if (!this.types.isJSXIdentifier(attr.name)) continue;
|
|
336
|
+
if (attr.name.name !== paramName) continue;
|
|
337
|
+
|
|
338
|
+
if (
|
|
339
|
+
attr.value &&
|
|
340
|
+
this.types.isJSXExpressionContainer(attr.value) &&
|
|
341
|
+
this.types.isExpression(attr.value.expression)
|
|
342
|
+
) {
|
|
343
|
+
const exprPath = jsxPath
|
|
344
|
+
.get("attributes")
|
|
345
|
+
.find(
|
|
346
|
+
(a) =>
|
|
347
|
+
a.isJSXAttribute() &&
|
|
348
|
+
this.types.isJSXIdentifier(a.node.name) &&
|
|
349
|
+
a.node.name.name === paramName
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
if (exprPath && exprPath.isJSXAttribute()) {
|
|
353
|
+
const valPath = exprPath.get("value");
|
|
354
|
+
if (
|
|
355
|
+
valPath.isJSXExpressionContainer()
|
|
356
|
+
) {
|
|
357
|
+
const expr = valPath.get("expression");
|
|
358
|
+
if (expr.isExpression()) {
|
|
359
|
+
result = this.traceExpression(expr);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private traceArrayExpression(
|
|
372
|
+
path: NodePath<t.ArrayExpression>
|
|
373
|
+
): CollectionInfo | null {
|
|
374
|
+
const elements = path.get("elements");
|
|
375
|
+
for (const el of elements) {
|
|
376
|
+
if (el.isSpreadElement()) {
|
|
377
|
+
const arg = el.get("argument");
|
|
378
|
+
if (arg.isExpression()) {
|
|
379
|
+
const result = this.traceExpression(arg);
|
|
380
|
+
if (result) return result;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private getFunctionName(fn: NodePath<t.Function>): string | null {
|
|
388
|
+
if (fn.isFunctionDeclaration() && fn.node.id) {
|
|
389
|
+
return fn.node.id.name;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const parent = fn.parentPath;
|
|
393
|
+
if (parent?.isVariableDeclarator()) {
|
|
394
|
+
const id = parent.get("id");
|
|
395
|
+
if (id.isIdentifier()) return id.node.name;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private getJSXElementName(node: t.JSXOpeningElement): string | null {
|
|
402
|
+
if (this.types.isJSXIdentifier(node.name)) {
|
|
403
|
+
return node.name.name;
|
|
404
|
+
}
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
traceGetByIdSource(
|
|
409
|
+
path: NodePath
|
|
410
|
+
): CollectionInfo | null {
|
|
411
|
+
this.visitedGetByIdSetters.clear();
|
|
412
|
+
return this.findGetByIdInScope(path);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
private findGetByIdInScope(
|
|
416
|
+
path: NodePath
|
|
417
|
+
): CollectionInfo | null {
|
|
418
|
+
const fn = path.getFunctionParent() ?? path.scope.path;
|
|
419
|
+
let result: CollectionInfo | null = null;
|
|
420
|
+
|
|
421
|
+
fn.traverse({
|
|
422
|
+
CallExpression: (callPath: NodePath<t.CallExpression>) => {
|
|
423
|
+
if (result) return;
|
|
424
|
+
const info = this.callUtils.isGetByIdCall(callPath.node);
|
|
425
|
+
if (info) {
|
|
426
|
+
result = {
|
|
427
|
+
id: info.collectionName,
|
|
428
|
+
references: info.multiRefFields,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
return result;
|
|
435
|
+
}
|
|
436
|
+
}
|