@depup/base44__vite-plugin 1.0.4-depup.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/README.md +34 -0
- package/changes.json +22 -0
- package/compat/agents.cjs +13 -0
- package/compat/base44Client.cjs +6 -0
- package/compat/entities.cjs +25 -0
- package/compat/functions.cjs +9 -0
- package/compat/integrations.cjs +9 -0
- package/dist/ErrorOverlay.d.ts +12 -0
- package/dist/ErrorOverlay.d.ts.map +1 -0
- package/dist/ErrorOverlay.js +51 -0
- package/dist/ErrorOverlay.js.map +1 -0
- package/dist/bridge.d.ts +8 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +8 -0
- package/dist/bridge.js.map +1 -0
- package/dist/capabilities/inline-edit/controller.d.ts +3 -0
- package/dist/capabilities/inline-edit/controller.d.ts.map +1 -0
- package/dist/capabilities/inline-edit/controller.js +203 -0
- package/dist/capabilities/inline-edit/controller.js.map +1 -0
- package/dist/capabilities/inline-edit/dom-utils.d.ts +7 -0
- package/dist/capabilities/inline-edit/dom-utils.d.ts.map +1 -0
- package/dist/capabilities/inline-edit/dom-utils.js +59 -0
- package/dist/capabilities/inline-edit/dom-utils.js.map +1 -0
- package/dist/capabilities/inline-edit/index.d.ts +3 -0
- package/dist/capabilities/inline-edit/index.d.ts.map +1 -0
- package/dist/capabilities/inline-edit/index.js +2 -0
- package/dist/capabilities/inline-edit/index.js.map +1 -0
- package/dist/capabilities/inline-edit/types.d.ts +29 -0
- package/dist/capabilities/inline-edit/types.d.ts.map +1 -0
- package/dist/capabilities/inline-edit/types.js +2 -0
- package/dist/capabilities/inline-edit/types.js.map +1 -0
- package/dist/consts.d.ts +11 -0
- package/dist/consts.d.ts.map +1 -0
- package/dist/consts.js +11 -0
- package/dist/consts.js.map +1 -0
- package/dist/error-overlay-plugin.d.ts +3 -0
- package/dist/error-overlay-plugin.d.ts.map +1 -0
- package/dist/error-overlay-plugin.js +15 -0
- package/dist/error-overlay-plugin.js.map +1 -0
- package/dist/html-injections-plugin.d.ts +8 -0
- package/dist/html-injections-plugin.d.ts.map +1 -0
- package/dist/html-injections-plugin.js +132 -0
- package/dist/html-injections-plugin.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +158 -0
- package/dist/index.js.map +1 -0
- package/dist/injections/layer-dropdown/consts.d.ts +20 -0
- package/dist/injections/layer-dropdown/consts.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/consts.js +41 -0
- package/dist/injections/layer-dropdown/consts.js.map +1 -0
- package/dist/injections/layer-dropdown/controller.d.ts +4 -0
- package/dist/injections/layer-dropdown/controller.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/controller.js +88 -0
- package/dist/injections/layer-dropdown/controller.js.map +1 -0
- package/dist/injections/layer-dropdown/dropdown-ui.d.ts +13 -0
- package/dist/injections/layer-dropdown/dropdown-ui.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/dropdown-ui.js +186 -0
- package/dist/injections/layer-dropdown/dropdown-ui.js.map +1 -0
- package/dist/injections/layer-dropdown/types.d.ts +26 -0
- package/dist/injections/layer-dropdown/types.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/types.js +3 -0
- package/dist/injections/layer-dropdown/types.js.map +1 -0
- package/dist/injections/layer-dropdown/utils.d.ts +25 -0
- package/dist/injections/layer-dropdown/utils.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/utils.js +143 -0
- package/dist/injections/layer-dropdown/utils.js.map +1 -0
- package/dist/injections/navigation-notifier.d.ts +2 -0
- package/dist/injections/navigation-notifier.d.ts.map +1 -0
- package/dist/injections/navigation-notifier.js +34 -0
- package/dist/injections/navigation-notifier.js.map +1 -0
- package/dist/injections/sandbox-hmr-notifier.d.ts +2 -0
- package/dist/injections/sandbox-hmr-notifier.d.ts.map +1 -0
- package/dist/injections/sandbox-hmr-notifier.js +10 -0
- package/dist/injections/sandbox-hmr-notifier.js.map +1 -0
- package/dist/injections/sandbox-mount-observer.d.ts +2 -0
- package/dist/injections/sandbox-mount-observer.d.ts.map +1 -0
- package/dist/injections/sandbox-mount-observer.js +18 -0
- package/dist/injections/sandbox-mount-observer.js.map +1 -0
- package/dist/injections/unhandled-errors-handlers.d.ts +2 -0
- package/dist/injections/unhandled-errors-handlers.d.ts.map +1 -0
- package/dist/injections/unhandled-errors-handlers.js +93 -0
- package/dist/injections/unhandled-errors-handlers.js.map +1 -0
- package/dist/injections/utils.d.ts +65 -0
- package/dist/injections/utils.d.ts.map +1 -0
- package/dist/injections/utils.js +186 -0
- package/dist/injections/utils.js.map +1 -0
- package/dist/injections/visual-edit-agent.d.ts +2 -0
- package/dist/injections/visual-edit-agent.d.ts.map +1 -0
- package/dist/injections/visual-edit-agent.js +583 -0
- package/dist/injections/visual-edit-agent.js.map +1 -0
- package/dist/jsx-processor.d.ts +17 -0
- package/dist/jsx-processor.d.ts.map +1 -0
- package/dist/jsx-processor.js +129 -0
- package/dist/jsx-processor.js.map +1 -0
- package/dist/jsx-utils.d.ts +16 -0
- package/dist/jsx-utils.d.ts.map +1 -0
- package/dist/jsx-utils.js +98 -0
- package/dist/jsx-utils.js.map +1 -0
- 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 +39 -0
- package/dist/processors/collection-item-field-processor.d.ts.map +1 -0
- package/dist/processors/collection-item-field-processor.js +289 -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/static-array-processor.d.ts +28 -0
- package/dist/processors/static-array-processor.d.ts.map +1 -0
- package/dist/processors/static-array-processor.js +173 -0
- package/dist/processors/static-array-processor.js.map +1 -0
- package/dist/processors/utils/collection-tracing-utils.d.ts +36 -0
- package/dist/processors/utils/collection-tracing-utils.d.ts.map +1 -0
- package/dist/processors/utils/collection-tracing-utils.js +390 -0
- package/dist/processors/utils/collection-tracing-utils.js.map +1 -0
- package/dist/processors/utils/shared-utils.d.ts +96 -0
- package/dist/processors/utils/shared-utils.d.ts.map +1 -0
- package/dist/processors/utils/shared-utils.js +600 -0
- package/dist/processors/utils/shared-utils.js.map +1 -0
- package/dist/statics/index.mjs +16 -0
- package/dist/statics/index.mjs.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +22 -0
- package/dist/utils.js.map +1 -0
- package/dist/visual-edit-plugin.d.ts +3 -0
- package/dist/visual-edit-plugin.d.ts.map +1 -0
- package/dist/visual-edit-plugin.js +100 -0
- package/dist/visual-edit-plugin.js.map +1 -0
- package/package.json +75 -0
- package/src/ErrorOverlay.ts +71 -0
- package/src/bridge.ts +8 -0
- package/src/capabilities/inline-edit/controller.ts +254 -0
- package/src/capabilities/inline-edit/dom-utils.ts +58 -0
- package/src/capabilities/inline-edit/index.ts +2 -0
- package/src/capabilities/inline-edit/types.ts +35 -0
- package/src/consts.ts +11 -0
- package/src/error-overlay-plugin.ts +19 -0
- package/src/html-injections-plugin.ts +166 -0
- package/src/index.ts +225 -0
- package/src/injections/layer-dropdown/LAYERS.md +258 -0
- package/src/injections/layer-dropdown/consts.ts +51 -0
- package/src/injections/layer-dropdown/controller.ts +109 -0
- package/src/injections/layer-dropdown/dropdown-ui.ts +242 -0
- package/src/injections/layer-dropdown/types.ts +30 -0
- package/src/injections/layer-dropdown/utils.ts +175 -0
- package/src/injections/navigation-notifier.ts +43 -0
- package/src/injections/sandbox-hmr-notifier.ts +8 -0
- package/src/injections/sandbox-mount-observer.ts +25 -0
- package/src/injections/unhandled-errors-handlers.ts +114 -0
- package/src/injections/utils.ts +208 -0
- package/src/injections/visual-edit-agent.ts +706 -0
- package/src/jsx-processor.ts +169 -0
- package/src/jsx-utils.ts +131 -0
- package/src/processors/collection-id-processor.ts +261 -0
- package/src/processors/collection-item-field-processor.ts +439 -0
- package/src/processors/collection-item-id-processor.ts +69 -0
- package/src/processors/static-array-processor.ts +260 -0
- package/src/processors/utils/collection-tracing-utils.ts +507 -0
- package/src/processors/utils/shared-utils.ts +785 -0
- package/src/utils.ts +27 -0
- package/src/visual-edit-plugin.md +358 -0
- package/src/visual-edit-plugin.ts +110 -0
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
import type { NodePath } from "@babel/traverse";
|
|
2
|
+
import type * as t from "@babel/types";
|
|
3
|
+
import { JSXUtils } from "../../jsx-utils.js";
|
|
4
|
+
import { ALLOWED_CUSTOM_COMPONENTS, EXCLUDED_FIELDS } from "../../consts.js";
|
|
5
|
+
|
|
6
|
+
export class JSXAttributeUtils {
|
|
7
|
+
constructor(private types: typeof t) {}
|
|
8
|
+
|
|
9
|
+
hasAttribute(
|
|
10
|
+
path: NodePath<t.JSXOpeningElement>,
|
|
11
|
+
attributeName: string
|
|
12
|
+
): boolean {
|
|
13
|
+
return path.node.attributes.some(
|
|
14
|
+
(attr: t.JSXAttribute | t.JSXSpreadAttribute) =>
|
|
15
|
+
this.types.isJSXAttribute(attr) &&
|
|
16
|
+
JSXUtils.getAttributeName(attr) === attributeName
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getAttributeValue(
|
|
21
|
+
path: NodePath<t.JSXOpeningElement>,
|
|
22
|
+
attributeName: string
|
|
23
|
+
): t.JSXAttribute["value"] | null {
|
|
24
|
+
for (const attr of path.node.attributes) {
|
|
25
|
+
if (
|
|
26
|
+
this.types.isJSXAttribute(attr) &&
|
|
27
|
+
JSXUtils.getAttributeName(attr) === attributeName
|
|
28
|
+
) {
|
|
29
|
+
return attr.value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getAttributeStringValue(
|
|
36
|
+
path: NodePath<t.JSXOpeningElement>,
|
|
37
|
+
attributeName: string
|
|
38
|
+
): string | null {
|
|
39
|
+
const value = this.getAttributeValue(path, attributeName);
|
|
40
|
+
if (value && this.types.isStringLiteral(value)) {
|
|
41
|
+
return value.value;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
addStringAttribute(
|
|
47
|
+
path: NodePath<t.JSXOpeningElement>,
|
|
48
|
+
attributeName: string,
|
|
49
|
+
value: string
|
|
50
|
+
): void {
|
|
51
|
+
if (!this.hasAttribute(path, attributeName)) {
|
|
52
|
+
path.node.attributes.push(
|
|
53
|
+
this.types.jsxAttribute(
|
|
54
|
+
this.types.jsxIdentifier(attributeName),
|
|
55
|
+
this.types.stringLiteral(value)
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
addExpressionAttribute(
|
|
62
|
+
path: NodePath<t.JSXOpeningElement>,
|
|
63
|
+
attributeName: string,
|
|
64
|
+
expression: t.Expression
|
|
65
|
+
): void {
|
|
66
|
+
if (!this.hasAttribute(path, attributeName)) {
|
|
67
|
+
path.node.attributes.push(
|
|
68
|
+
this.types.jsxAttribute(
|
|
69
|
+
this.types.jsxIdentifier(attributeName),
|
|
70
|
+
this.types.jsxExpressionContainer(expression)
|
|
71
|
+
)
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
findAncestorWithAttribute(
|
|
77
|
+
path: NodePath,
|
|
78
|
+
attributeName: string
|
|
79
|
+
): NodePath<t.JSXOpeningElement> | null {
|
|
80
|
+
let current: NodePath | null = path.parentPath;
|
|
81
|
+
while (current) {
|
|
82
|
+
if (current.isJSXElement()) {
|
|
83
|
+
const opening = current.get("openingElement");
|
|
84
|
+
if (this.hasAttribute(opening, attributeName)) {
|
|
85
|
+
return opening;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
current = current.parentPath;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export class PathNavigationUtils {
|
|
95
|
+
constructor(private types: typeof t) {}
|
|
96
|
+
|
|
97
|
+
findParentJSXElement(
|
|
98
|
+
path: NodePath
|
|
99
|
+
): NodePath<t.JSXElement> | null {
|
|
100
|
+
let current: NodePath | null = path.parentPath;
|
|
101
|
+
while (current) {
|
|
102
|
+
if (current.isJSXElement()) return current;
|
|
103
|
+
current = current.parentPath;
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
findEnclosingFunction(
|
|
109
|
+
path: NodePath
|
|
110
|
+
): NodePath<t.Function> | null {
|
|
111
|
+
let current: NodePath | null = path.parentPath;
|
|
112
|
+
while (current) {
|
|
113
|
+
if (current.isFunction()) return current as NodePath<t.Function>;
|
|
114
|
+
current = current.parentPath;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
findReturnStatement(
|
|
120
|
+
path: NodePath
|
|
121
|
+
): NodePath<t.ReturnStatement> | null {
|
|
122
|
+
let current: NodePath | null = path.parentPath;
|
|
123
|
+
while (current) {
|
|
124
|
+
if (current.isReturnStatement()) return current;
|
|
125
|
+
current = current.parentPath;
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
isRootReturnElement(
|
|
131
|
+
path: NodePath<t.JSXOpeningElement>
|
|
132
|
+
): boolean {
|
|
133
|
+
const jsxElement = path.parentPath;
|
|
134
|
+
if (!jsxElement?.isJSXElement()) return false;
|
|
135
|
+
|
|
136
|
+
const parent = jsxElement.parentPath;
|
|
137
|
+
if (!parent) return false;
|
|
138
|
+
|
|
139
|
+
if (parent.isReturnStatement()) return true;
|
|
140
|
+
|
|
141
|
+
if (parent.isArrowFunctionExpression()) return true;
|
|
142
|
+
|
|
143
|
+
if (parent.isParenthesizedExpression()) {
|
|
144
|
+
const grandparent = parent.parentPath;
|
|
145
|
+
if (grandparent?.isReturnStatement()) return true;
|
|
146
|
+
if (grandparent?.isArrowFunctionExpression()) return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
findDOMElementTarget(
|
|
153
|
+
path: NodePath<t.JSXOpeningElement>
|
|
154
|
+
): NodePath<t.JSXOpeningElement> | null {
|
|
155
|
+
if (this.isDOMOrAllowedElement(path)) return path;
|
|
156
|
+
|
|
157
|
+
let current: NodePath | null = path.parentPath;
|
|
158
|
+
while (current) {
|
|
159
|
+
if (current.isJSXElement()) {
|
|
160
|
+
const opening = current.get("openingElement");
|
|
161
|
+
if (this.isDOMOrAllowedElement(opening)) return opening;
|
|
162
|
+
}
|
|
163
|
+
current = current.parentPath;
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private isDOMOrAllowedElement(
|
|
169
|
+
path: NodePath<t.JSXOpeningElement>
|
|
170
|
+
): boolean {
|
|
171
|
+
const name = JSXUtils.getElementName(path.node);
|
|
172
|
+
if (!name) return false;
|
|
173
|
+
|
|
174
|
+
if (name.charAt(0) === name.charAt(0).toLowerCase()) return true;
|
|
175
|
+
|
|
176
|
+
return ALLOWED_CUSTOM_COMPONENTS.includes(name);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export class ExpressionAnalysisUtils {
|
|
181
|
+
constructor(private types: typeof t) {}
|
|
182
|
+
|
|
183
|
+
isIdAccess(node: t.Expression): boolean {
|
|
184
|
+
if (!this.types.isMemberExpression(node)) return false;
|
|
185
|
+
if (this.types.isIdentifier(node.property)) {
|
|
186
|
+
return node.property.name === "_id" || node.property.name === "id";
|
|
187
|
+
}
|
|
188
|
+
if (this.types.isStringLiteral(node.property)) {
|
|
189
|
+
return node.property.value === "_id" || node.property.value === "id";
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
isItemsAccess(node: t.Expression): boolean {
|
|
195
|
+
return (
|
|
196
|
+
this.types.isMemberExpression(node) &&
|
|
197
|
+
this.types.isIdentifier(node.property) &&
|
|
198
|
+
node.property.name === "items"
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
isLengthAccess(node: t.Expression): boolean {
|
|
203
|
+
return (
|
|
204
|
+
this.types.isMemberExpression(node) &&
|
|
205
|
+
this.types.isIdentifier(node.property) &&
|
|
206
|
+
node.property.name === "length"
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
extractRootIdentifier(node: t.Expression): t.Identifier | null {
|
|
211
|
+
if (this.types.isIdentifier(node)) return node;
|
|
212
|
+
if (this.types.isMemberExpression(node)) {
|
|
213
|
+
return this.extractRootIdentifier(node.object as t.Expression);
|
|
214
|
+
}
|
|
215
|
+
if (this.types.isOptionalMemberExpression(node)) {
|
|
216
|
+
return this.extractRootIdentifier(node.object as t.Expression);
|
|
217
|
+
}
|
|
218
|
+
if (this.types.isCallExpression(node) && this.types.isMemberExpression(node.callee)) {
|
|
219
|
+
return this.extractRootIdentifier(node.callee.object as t.Expression);
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
unwrapLogicalExpression(node: t.Expression): t.Expression {
|
|
225
|
+
if (this.types.isLogicalExpression(node)) {
|
|
226
|
+
if (node.operator === "&&") return node.right as t.Expression;
|
|
227
|
+
if (node.operator === "||" || node.operator === "??") {
|
|
228
|
+
return node.left as t.Expression;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return node;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
collectMemberExpressionPath(node: t.Expression): string[] {
|
|
235
|
+
const parts: string[] = [];
|
|
236
|
+
let current: t.Expression = node;
|
|
237
|
+
|
|
238
|
+
while (
|
|
239
|
+
this.types.isMemberExpression(current) ||
|
|
240
|
+
this.types.isOptionalMemberExpression(current)
|
|
241
|
+
) {
|
|
242
|
+
const prop = (current as t.MemberExpression).property;
|
|
243
|
+
if (this.types.isIdentifier(prop)) {
|
|
244
|
+
parts.unshift(prop.name);
|
|
245
|
+
} else if (this.types.isStringLiteral(prop)) {
|
|
246
|
+
parts.unshift(prop.value);
|
|
247
|
+
}
|
|
248
|
+
current = (current as t.MemberExpression).object as t.Expression;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (this.types.isIdentifier(current)) {
|
|
252
|
+
parts.unshift(current.name);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return parts;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
createOptionalChainExpression(
|
|
259
|
+
node: t.MemberExpression
|
|
260
|
+
): t.OptionalMemberExpression {
|
|
261
|
+
const object = this.types.isOptionalMemberExpression(node.object)
|
|
262
|
+
? node.object
|
|
263
|
+
: this.types.isMemberExpression(node.object)
|
|
264
|
+
? this.createOptionalChainExpression(node.object)
|
|
265
|
+
: node.object;
|
|
266
|
+
|
|
267
|
+
const property = node.property as t.Expression;
|
|
268
|
+
|
|
269
|
+
return this.types.optionalMemberExpression(
|
|
270
|
+
object,
|
|
271
|
+
property,
|
|
272
|
+
node.computed,
|
|
273
|
+
true
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
getFieldPathFromExpression(
|
|
278
|
+
node: t.Expression,
|
|
279
|
+
rootName: string
|
|
280
|
+
): string | null {
|
|
281
|
+
const parts = this.collectMemberExpressionPath(node);
|
|
282
|
+
if (parts.length < 2) return null;
|
|
283
|
+
if (parts[0] !== rootName) return null;
|
|
284
|
+
|
|
285
|
+
const fieldParts = parts.slice(1);
|
|
286
|
+
const fieldPath = fieldParts.join(".");
|
|
287
|
+
|
|
288
|
+
if (EXCLUDED_FIELDS.includes(fieldPath)) return null;
|
|
289
|
+
return fieldPath;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export class BindingUtils {
|
|
294
|
+
constructor(private types: typeof t) {}
|
|
295
|
+
|
|
296
|
+
isFunctionParameter(
|
|
297
|
+
identifierName: string,
|
|
298
|
+
path: NodePath
|
|
299
|
+
): boolean {
|
|
300
|
+
const fn = path.getFunctionParent();
|
|
301
|
+
if (!fn) return false;
|
|
302
|
+
|
|
303
|
+
const params = fn.get("params");
|
|
304
|
+
for (const param of (Array.isArray(params) ? params : [params])) {
|
|
305
|
+
if (param.isIdentifier() && param.node.name === identifierName) {
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
if (param.isObjectPattern()) {
|
|
309
|
+
for (const prop of param.get("properties")) {
|
|
310
|
+
if (
|
|
311
|
+
prop.isObjectProperty() &&
|
|
312
|
+
this.types.isIdentifier(prop.node.value) &&
|
|
313
|
+
prop.node.value.name === identifierName
|
|
314
|
+
) {
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (param.isArrayPattern()) {
|
|
320
|
+
for (const el of param.get("elements")) {
|
|
321
|
+
if (el.isIdentifier() && el.node.name === identifierName) {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
isUseStateCall(
|
|
331
|
+
init: NodePath<t.Node>
|
|
332
|
+
): { stateIndex: number; setterName: string | null } | null {
|
|
333
|
+
if (!init.isCallExpression()) return null;
|
|
334
|
+
|
|
335
|
+
const callee = init.get("callee");
|
|
336
|
+
if (!callee.isIdentifier() || callee.node.name !== "useState") return null;
|
|
337
|
+
|
|
338
|
+
const declarator = init.parentPath;
|
|
339
|
+
if (!declarator?.isVariableDeclarator()) return null;
|
|
340
|
+
|
|
341
|
+
const id = declarator.get("id");
|
|
342
|
+
if (!id.isArrayPattern()) return null;
|
|
343
|
+
|
|
344
|
+
const elements = id.get("elements");
|
|
345
|
+
const setterEl = elements[1];
|
|
346
|
+
const setterName =
|
|
347
|
+
setterEl && setterEl.isIdentifier() ? setterEl.node.name : null;
|
|
348
|
+
|
|
349
|
+
return { stateIndex: 0, setterName };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
isPromiseAllCall(
|
|
353
|
+
init: NodePath<t.Node>
|
|
354
|
+
): boolean {
|
|
355
|
+
if (!init.isAwaitExpression()) {
|
|
356
|
+
if (init.isCallExpression()) {
|
|
357
|
+
const callee = init.get("callee");
|
|
358
|
+
return this.isPromiseAllCallee(callee);
|
|
359
|
+
}
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const argument = init.get("argument");
|
|
364
|
+
if (!argument.isCallExpression()) return false;
|
|
365
|
+
|
|
366
|
+
const callee = argument.get("callee");
|
|
367
|
+
return this.isPromiseAllCallee(callee);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private isPromiseAllCallee(callee: NodePath): boolean {
|
|
371
|
+
if (!callee.isMemberExpression()) return false;
|
|
372
|
+
const obj = callee.get("object") as NodePath;
|
|
373
|
+
const prop = callee.get("property") as NodePath;
|
|
374
|
+
return (
|
|
375
|
+
obj.isIdentifier() &&
|
|
376
|
+
obj.node.name === "Promise" &&
|
|
377
|
+
prop.isIdentifier() &&
|
|
378
|
+
prop.node.name === "all"
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
extractDestructuredProperties(
|
|
383
|
+
pattern: t.ObjectPattern
|
|
384
|
+
): string[] {
|
|
385
|
+
const properties: string[] = [];
|
|
386
|
+
for (const prop of pattern.properties) {
|
|
387
|
+
if (this.types.isObjectProperty(prop)) {
|
|
388
|
+
if (this.types.isIdentifier(prop.key)) {
|
|
389
|
+
properties.push(prop.key.name);
|
|
390
|
+
} else if (this.types.isStringLiteral(prop.key)) {
|
|
391
|
+
properties.push(prop.key.value);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return properties;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
findSetterCallInScope(
|
|
399
|
+
setterName: string,
|
|
400
|
+
scope: NodePath
|
|
401
|
+
): NodePath<t.CallExpression> | null {
|
|
402
|
+
let result: NodePath<t.CallExpression> | null = null;
|
|
403
|
+
|
|
404
|
+
scope.traverse({
|
|
405
|
+
CallExpression(callPath) {
|
|
406
|
+
if (result) return;
|
|
407
|
+
const callee = callPath.get("callee");
|
|
408
|
+
if (callee.isIdentifier() && callee.node.name === setterName) {
|
|
409
|
+
result = callPath;
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
return result;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export class CallExpressionUtils {
|
|
419
|
+
constructor(private types: typeof t) {}
|
|
420
|
+
|
|
421
|
+
isGetAllCall(
|
|
422
|
+
node: t.CallExpression
|
|
423
|
+
): { collectionName: string; references: string[] } | null {
|
|
424
|
+
const callee = node.callee;
|
|
425
|
+
if (!this.types.isMemberExpression(callee)) return null;
|
|
426
|
+
|
|
427
|
+
const obj = callee.object;
|
|
428
|
+
const prop = callee.property;
|
|
429
|
+
|
|
430
|
+
if (
|
|
431
|
+
!this.types.isIdentifier(obj) ||
|
|
432
|
+
!this.types.isIdentifier(prop) ||
|
|
433
|
+
prop.name !== "getAll"
|
|
434
|
+
) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const args = node.arguments;
|
|
439
|
+
if (args.length < 1) return null;
|
|
440
|
+
|
|
441
|
+
const firstArg = args[0];
|
|
442
|
+
if (!this.types.isStringLiteral(firstArg)) return null;
|
|
443
|
+
|
|
444
|
+
const collectionName = firstArg.value;
|
|
445
|
+
const references = this.extractReferencesFromArg(args[1]);
|
|
446
|
+
|
|
447
|
+
return { collectionName, references };
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
isGetByIdCall(
|
|
451
|
+
node: t.CallExpression
|
|
452
|
+
): {
|
|
453
|
+
collectionName: string;
|
|
454
|
+
multiRefFields: string[];
|
|
455
|
+
} | null {
|
|
456
|
+
const callee = node.callee;
|
|
457
|
+
if (!this.types.isMemberExpression(callee)) return null;
|
|
458
|
+
|
|
459
|
+
const obj = callee.object;
|
|
460
|
+
const prop = callee.property;
|
|
461
|
+
|
|
462
|
+
if (
|
|
463
|
+
!this.types.isIdentifier(obj) ||
|
|
464
|
+
!this.types.isIdentifier(prop) ||
|
|
465
|
+
prop.name !== "getById"
|
|
466
|
+
) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const args = node.arguments;
|
|
471
|
+
if (args.length < 1) return null;
|
|
472
|
+
|
|
473
|
+
const firstArg = args[0];
|
|
474
|
+
if (!this.types.isStringLiteral(firstArg)) return null;
|
|
475
|
+
|
|
476
|
+
const collectionName = firstArg.value;
|
|
477
|
+
const multiRefFields = this.extractMultiRefFromOptions(args[2]);
|
|
478
|
+
|
|
479
|
+
return { collectionName, multiRefFields };
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private extractReferencesFromArg(
|
|
483
|
+
arg: t.Expression | t.SpreadElement | t.ArgumentPlaceholder | undefined
|
|
484
|
+
): string[] {
|
|
485
|
+
if (!arg || !this.types.isArrayExpression(arg)) return [];
|
|
486
|
+
|
|
487
|
+
return arg.elements
|
|
488
|
+
.filter((el): el is t.StringLiteral => this.types.isStringLiteral(el))
|
|
489
|
+
.map((el) => el.value);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
private extractMultiRefFromOptions(
|
|
493
|
+
arg: t.Expression | t.SpreadElement | t.ArgumentPlaceholder | undefined
|
|
494
|
+
): string[] {
|
|
495
|
+
if (!arg || !this.types.isObjectExpression(arg)) return [];
|
|
496
|
+
|
|
497
|
+
for (const prop of arg.properties) {
|
|
498
|
+
if (
|
|
499
|
+
this.types.isObjectProperty(prop) &&
|
|
500
|
+
this.types.isIdentifier(prop.key) &&
|
|
501
|
+
prop.key.name === "multiRef" &&
|
|
502
|
+
this.types.isArrayExpression(prop.value)
|
|
503
|
+
) {
|
|
504
|
+
return prop.value.elements
|
|
505
|
+
.filter((el): el is t.StringLiteral =>
|
|
506
|
+
this.types.isStringLiteral(el)
|
|
507
|
+
)
|
|
508
|
+
.map((el) => el.value);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return [];
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
isArrayMethod(
|
|
515
|
+
node: t.CallExpression,
|
|
516
|
+
methodName: string
|
|
517
|
+
): boolean {
|
|
518
|
+
const callee = node.callee;
|
|
519
|
+
return (
|
|
520
|
+
this.types.isMemberExpression(callee) &&
|
|
521
|
+
this.types.isIdentifier(callee.property) &&
|
|
522
|
+
callee.property.name === methodName
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
isChainedArrayMethod(node: t.CallExpression): boolean {
|
|
527
|
+
const chainMethods = ["filter", "sort", "slice", "concat", "reverse", "flat"];
|
|
528
|
+
return chainMethods.some((m) => this.isArrayMethod(node, m));
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Detect base44.entities.EntityName.list() or .getAll() patterns.
|
|
533
|
+
* Returns the entity name as the collection name.
|
|
534
|
+
*/
|
|
535
|
+
isBase44EntityListCall(
|
|
536
|
+
node: t.CallExpression
|
|
537
|
+
): { collectionName: string } | null {
|
|
538
|
+
const callee = node.callee;
|
|
539
|
+
if (!this.types.isMemberExpression(callee)) return null;
|
|
540
|
+
|
|
541
|
+
const method = callee.property;
|
|
542
|
+
if (
|
|
543
|
+
!this.types.isIdentifier(method) ||
|
|
544
|
+
(method.name !== "list" && method.name !== "getAll")
|
|
545
|
+
) {
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const entityAccess = callee.object;
|
|
550
|
+
if (!this.types.isMemberExpression(entityAccess)) return null;
|
|
551
|
+
|
|
552
|
+
const entityName = entityAccess.property;
|
|
553
|
+
if (!this.types.isIdentifier(entityName)) return null;
|
|
554
|
+
|
|
555
|
+
const entitiesAccess = entityAccess.object;
|
|
556
|
+
if (!this.types.isMemberExpression(entitiesAccess)) return null;
|
|
557
|
+
|
|
558
|
+
const entitiesProp = entitiesAccess.property;
|
|
559
|
+
if (
|
|
560
|
+
!this.types.isIdentifier(entitiesProp) ||
|
|
561
|
+
entitiesProp.name !== "entities"
|
|
562
|
+
) {
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return { collectionName: entityName.name };
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Detect base44.entities.EntityName.getById() or .get() patterns.
|
|
571
|
+
*/
|
|
572
|
+
isBase44EntityGetCall(
|
|
573
|
+
node: t.CallExpression
|
|
574
|
+
): { collectionName: string } | null {
|
|
575
|
+
const callee = node.callee;
|
|
576
|
+
if (!this.types.isMemberExpression(callee)) return null;
|
|
577
|
+
|
|
578
|
+
const method = callee.property;
|
|
579
|
+
if (
|
|
580
|
+
!this.types.isIdentifier(method) ||
|
|
581
|
+
(method.name !== "get" && method.name !== "getById")
|
|
582
|
+
) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const entityAccess = callee.object;
|
|
587
|
+
if (!this.types.isMemberExpression(entityAccess)) return null;
|
|
588
|
+
|
|
589
|
+
const entityName = entityAccess.property;
|
|
590
|
+
if (!this.types.isIdentifier(entityName)) return null;
|
|
591
|
+
|
|
592
|
+
const entitiesAccess = entityAccess.object;
|
|
593
|
+
if (!this.types.isMemberExpression(entitiesAccess)) return null;
|
|
594
|
+
|
|
595
|
+
const entitiesProp = entitiesAccess.property;
|
|
596
|
+
if (
|
|
597
|
+
!this.types.isIdentifier(entitiesProp) ||
|
|
598
|
+
entitiesProp.name !== "entities"
|
|
599
|
+
) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return { collectionName: entityName.name };
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
getCallbackArgument(
|
|
607
|
+
callExpr: t.CallExpression
|
|
608
|
+
): t.ArrowFunctionExpression | t.FunctionExpression | null {
|
|
609
|
+
const firstArg = callExpr.arguments[0];
|
|
610
|
+
if (
|
|
611
|
+
this.types.isArrowFunctionExpression(firstArg) ||
|
|
612
|
+
this.types.isFunctionExpression(firstArg)
|
|
613
|
+
) {
|
|
614
|
+
return firstArg;
|
|
615
|
+
}
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export class StaticValueUtils {
|
|
621
|
+
constructor(private types: typeof t) {}
|
|
622
|
+
|
|
623
|
+
isPrimitiveLiteral(path: NodePath<t.Node>): boolean {
|
|
624
|
+
return (
|
|
625
|
+
path.isStringLiteral() ||
|
|
626
|
+
path.isNumericLiteral() ||
|
|
627
|
+
path.isBooleanLiteral() ||
|
|
628
|
+
path.isNullLiteral()
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
isStaticValue(
|
|
633
|
+
path: NodePath<t.Node>,
|
|
634
|
+
visited: Set<string> = new Set()
|
|
635
|
+
): boolean {
|
|
636
|
+
if (this.isPrimitiveLiteral(path)) return true;
|
|
637
|
+
if (path.isIdentifier()) return this.isStaticIdentifier(path, visited);
|
|
638
|
+
if (path.isObjectExpression()) return this.isStaticObject(path, visited);
|
|
639
|
+
if (path.isArrayExpression())
|
|
640
|
+
return this.isStaticArrayExpression(path, visited);
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
isStaticIdentifier(
|
|
645
|
+
path: NodePath<t.Identifier>,
|
|
646
|
+
visited: Set<string> = new Set()
|
|
647
|
+
): boolean {
|
|
648
|
+
const binding = path.scope.getBinding(path.node.name);
|
|
649
|
+
if (!binding) return false;
|
|
650
|
+
|
|
651
|
+
if (binding.kind === "module") return true;
|
|
652
|
+
|
|
653
|
+
if (binding.kind === "const" && binding.path.isVariableDeclarator()) {
|
|
654
|
+
const name = path.node.name;
|
|
655
|
+
if (visited.has(name)) return false;
|
|
656
|
+
visited.add(name);
|
|
657
|
+
|
|
658
|
+
const init = binding.path.get("init");
|
|
659
|
+
if (init.hasNode()) {
|
|
660
|
+
return this.isStaticValue(init as NodePath<t.Node>, visited);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
isStaticObject(
|
|
668
|
+
path: NodePath<t.ObjectExpression>,
|
|
669
|
+
visited: Set<string> = new Set()
|
|
670
|
+
): boolean {
|
|
671
|
+
return path.get("properties").every((prop: NodePath) => {
|
|
672
|
+
if (!prop.isObjectProperty()) return false;
|
|
673
|
+
return this.isStaticValue(prop.get("value") as NodePath<t.Node>, visited);
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
isStaticArrayExpression(
|
|
678
|
+
arrayExpression: NodePath<t.ArrayExpression>,
|
|
679
|
+
visited: Set<string> = new Set()
|
|
680
|
+
): boolean {
|
|
681
|
+
return arrayExpression.get("elements").every((element) => {
|
|
682
|
+
if (!element.node || element.isSpreadElement()) return true;
|
|
683
|
+
return this.isStaticValue(element as unknown as NodePath<t.Node>, visited);
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
isDerivedFromStaticData(
|
|
688
|
+
identifierName: string,
|
|
689
|
+
path: NodePath
|
|
690
|
+
): boolean {
|
|
691
|
+
const binding = path.scope.getBinding(identifierName);
|
|
692
|
+
if (!binding) return false;
|
|
693
|
+
|
|
694
|
+
if (binding.path.isVariableDeclarator()) {
|
|
695
|
+
const init = binding.path.get("init");
|
|
696
|
+
if (init.isArrayExpression() || init.isObjectExpression()) {
|
|
697
|
+
return this.isStaticValue(init as NodePath<t.Node>);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const fnParent = path.getFunctionParent();
|
|
702
|
+
if (!fnParent) return false;
|
|
703
|
+
|
|
704
|
+
const params = fnParent.get("params");
|
|
705
|
+
for (const param of (Array.isArray(params) ? params : [params])) {
|
|
706
|
+
if (param.isIdentifier() && param.node.name === identifierName) {
|
|
707
|
+
const mapCall = fnParent.parentPath;
|
|
708
|
+
if (mapCall?.isCallExpression()) {
|
|
709
|
+
const callee = mapCall.get("callee");
|
|
710
|
+
if (
|
|
711
|
+
callee.isMemberExpression() &&
|
|
712
|
+
(callee.get("property") as NodePath).isIdentifier()
|
|
713
|
+
) {
|
|
714
|
+
const propName = ((callee.get("property") as NodePath).node as t.Identifier).name;
|
|
715
|
+
if (propName === "map" || propName === "flatMap") {
|
|
716
|
+
const arrayObj = callee.get("object") as NodePath<t.Expression>;
|
|
717
|
+
if (arrayObj.isIdentifier()) {
|
|
718
|
+
return this.isDerivedFromStaticData(arrayObj.node.name, arrayObj);
|
|
719
|
+
}
|
|
720
|
+
if (arrayObj.isArrayExpression()) {
|
|
721
|
+
return this.isStaticArrayExpression(arrayObj);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
export class TypeCheckUtils {
|
|
734
|
+
constructor(private types: typeof t) {}
|
|
735
|
+
|
|
736
|
+
isArrayIsArrayCheck(node: t.Expression): boolean {
|
|
737
|
+
if (!this.types.isCallExpression(node)) return false;
|
|
738
|
+
|
|
739
|
+
const callee = node.callee;
|
|
740
|
+
return (
|
|
741
|
+
this.types.isMemberExpression(callee) &&
|
|
742
|
+
this.types.isIdentifier(callee.object) &&
|
|
743
|
+
callee.object.name === "Array" &&
|
|
744
|
+
this.types.isIdentifier(callee.property) &&
|
|
745
|
+
callee.property.name === "isArray"
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
isTypeofObjectCheck(node: t.Expression): boolean {
|
|
750
|
+
if (!this.types.isBinaryExpression(node)) return false;
|
|
751
|
+
if (node.operator !== "===" && node.operator !== "==") return false;
|
|
752
|
+
|
|
753
|
+
const isTypeof = (side: t.Expression) =>
|
|
754
|
+
this.types.isUnaryExpression(side) && side.operator === "typeof";
|
|
755
|
+
const isObjectString = (side: t.Expression) =>
|
|
756
|
+
this.types.isStringLiteral(side) && side.value === "object";
|
|
757
|
+
|
|
758
|
+
return (
|
|
759
|
+
(isTypeof(node.left as t.Expression) &&
|
|
760
|
+
isObjectString(node.right as t.Expression)) ||
|
|
761
|
+
(isObjectString(node.left as t.Expression) &&
|
|
762
|
+
isTypeof(node.right as t.Expression))
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
isReferenceTypeCheck(node: t.Expression): boolean {
|
|
767
|
+
return this.isArrayIsArrayCheck(node) || this.isTypeofObjectCheck(node);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
isLengthCheck(node: t.Expression): boolean {
|
|
771
|
+
if (this.types.isMemberExpression(node)) {
|
|
772
|
+
return (
|
|
773
|
+
this.types.isIdentifier(node.property) &&
|
|
774
|
+
node.property.name === "length"
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
if (this.types.isOptionalMemberExpression(node)) {
|
|
778
|
+
return (
|
|
779
|
+
this.types.isIdentifier(node.property) &&
|
|
780
|
+
node.property.name === "length"
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
}
|