@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,169 @@
|
|
|
1
|
+
import type { NodePath } from "@babel/traverse";
|
|
2
|
+
import type * as t from "@babel/types";
|
|
3
|
+
import { CollectionIdProcessor } from "./processors/collection-id-processor.js";
|
|
4
|
+
import { DataItemIdProcessor } from "./processors/collection-item-id-processor.js";
|
|
5
|
+
import { DataItemFieldProcessor } from "./processors/collection-item-field-processor.js";
|
|
6
|
+
import { StaticArrayProcessor } from "./processors/static-array-processor.js";
|
|
7
|
+
|
|
8
|
+
export class JSXProcessor {
|
|
9
|
+
private collectionIdProcessor: CollectionIdProcessor;
|
|
10
|
+
private dataItemIdProcessor: DataItemIdProcessor;
|
|
11
|
+
private dataItemFieldProcessor: DataItemFieldProcessor;
|
|
12
|
+
private staticArrayProcessor: StaticArrayProcessor;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
private types: typeof t,
|
|
16
|
+
private filename: string
|
|
17
|
+
) {
|
|
18
|
+
this.collectionIdProcessor = new CollectionIdProcessor(types);
|
|
19
|
+
this.dataItemIdProcessor = new DataItemIdProcessor(types);
|
|
20
|
+
this.dataItemFieldProcessor = new DataItemFieldProcessor(types);
|
|
21
|
+
this.staticArrayProcessor = new StaticArrayProcessor(types);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
processJSXElement(path: NodePath<t.JSXOpeningElement>): void {
|
|
25
|
+
if (this.hasAttribute(path, "data-source-location")) return;
|
|
26
|
+
|
|
27
|
+
this.addSourceLocationAttribute(path);
|
|
28
|
+
this.addDynamicContentAttribute(path);
|
|
29
|
+
|
|
30
|
+
this.collectionIdProcessor.process(path);
|
|
31
|
+
this.dataItemIdProcessor.process(path);
|
|
32
|
+
this.dataItemFieldProcessor.process(path);
|
|
33
|
+
this.staticArrayProcessor.process(path);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private addSourceLocationAttribute(
|
|
37
|
+
path: NodePath<t.JSXOpeningElement>
|
|
38
|
+
): void {
|
|
39
|
+
const { line, column } = path.node.loc?.start || { line: 1, column: 0 };
|
|
40
|
+
const value = `${this.filename}:${line}:${column}`;
|
|
41
|
+
|
|
42
|
+
path.node.attributes.unshift(
|
|
43
|
+
this.types.jsxAttribute(
|
|
44
|
+
this.types.jsxIdentifier("data-source-location"),
|
|
45
|
+
this.types.stringLiteral(value)
|
|
46
|
+
)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private addDynamicContentAttribute(
|
|
51
|
+
path: NodePath<t.JSXOpeningElement>
|
|
52
|
+
): void {
|
|
53
|
+
const parentElement = path.parentPath;
|
|
54
|
+
if (!parentElement?.isJSXElement()) return;
|
|
55
|
+
|
|
56
|
+
const isDynamic = this.checkIfElementHasDynamicContent(
|
|
57
|
+
parentElement.node as t.JSXElement
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const sourceLocIdx = path.node.attributes.findIndex(
|
|
61
|
+
(attr) =>
|
|
62
|
+
this.types.isJSXAttribute(attr) &&
|
|
63
|
+
this.types.isJSXIdentifier(attr.name) &&
|
|
64
|
+
attr.name.name === "data-source-location"
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
path.node.attributes.splice(
|
|
68
|
+
sourceLocIdx + 1,
|
|
69
|
+
0,
|
|
70
|
+
this.types.jsxAttribute(
|
|
71
|
+
this.types.jsxIdentifier("data-dynamic-content"),
|
|
72
|
+
this.types.stringLiteral(isDynamic ? "true" : "false")
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private hasAttribute(
|
|
78
|
+
path: NodePath<t.JSXOpeningElement>,
|
|
79
|
+
name: string
|
|
80
|
+
): boolean {
|
|
81
|
+
return path.node.attributes.some(
|
|
82
|
+
(attr: t.JSXAttribute | t.JSXSpreadAttribute) =>
|
|
83
|
+
this.types.isJSXAttribute(attr) &&
|
|
84
|
+
this.types.isJSXIdentifier(attr.name) &&
|
|
85
|
+
attr.name.name === name
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private checkIfElementHasDynamicContent(jsxElement: t.JSXElement): boolean {
|
|
90
|
+
let hasDynamicContent = false;
|
|
91
|
+
|
|
92
|
+
const checkNode = (node: t.Node): boolean => {
|
|
93
|
+
if (this.types.isJSXExpressionContainer(node)) {
|
|
94
|
+
const expression = (node as t.JSXExpressionContainer).expression;
|
|
95
|
+
if (this.types.isJSXEmptyExpression(expression)) return false;
|
|
96
|
+
if (!this.types.isLiteral(expression)) return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (
|
|
100
|
+
this.types.isTemplateLiteral(node) &&
|
|
101
|
+
(node as t.TemplateLiteral).expressions.length > 0
|
|
102
|
+
) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (this.types.isMemberExpression(node)) return true;
|
|
106
|
+
if (this.types.isCallExpression(node)) return true;
|
|
107
|
+
if (this.types.isConditionalExpression(node)) return true;
|
|
108
|
+
|
|
109
|
+
if (this.types.isIdentifier(node)) {
|
|
110
|
+
const dynamicNames = [
|
|
111
|
+
"props",
|
|
112
|
+
"state",
|
|
113
|
+
"data",
|
|
114
|
+
"item",
|
|
115
|
+
"value",
|
|
116
|
+
"text",
|
|
117
|
+
"content",
|
|
118
|
+
];
|
|
119
|
+
if (dynamicNames.some((name) => (node as t.Identifier).name.includes(name))) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return false;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const traverseNode = (node: Record<string, unknown>): void => {
|
|
128
|
+
if (hasDynamicContent) return;
|
|
129
|
+
if (checkNode(node as unknown as t.Node)) {
|
|
130
|
+
hasDynamicContent = true;
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
for (const key of Object.keys(node)) {
|
|
135
|
+
if (hasDynamicContent) return;
|
|
136
|
+
const value = node[key];
|
|
137
|
+
|
|
138
|
+
if (Array.isArray(value)) {
|
|
139
|
+
for (const child of value) {
|
|
140
|
+
if (child && typeof child === "object" && "type" in child) {
|
|
141
|
+
traverseNode(child as Record<string, unknown>);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} else if (value && typeof value === "object" && "type" in value) {
|
|
145
|
+
traverseNode(value as Record<string, unknown>);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const attributes = jsxElement.openingElement?.attributes || [];
|
|
151
|
+
for (const attr of attributes) {
|
|
152
|
+
if (hasDynamicContent) break;
|
|
153
|
+
if (this.types.isJSXSpreadAttribute(attr)) {
|
|
154
|
+
hasDynamicContent = true;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
if (this.types.isJSXAttribute(attr) && attr.value) {
|
|
158
|
+
traverseNode(attr.value as unknown as Record<string, unknown>);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for (const child of jsxElement.children) {
|
|
163
|
+
if (hasDynamicContent) break;
|
|
164
|
+
traverseNode(child as unknown as Record<string, unknown>);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return hasDynamicContent;
|
|
168
|
+
}
|
|
169
|
+
}
|
package/src/jsx-utils.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type * as t from "@babel/types";
|
|
2
|
+
import { ALLOWED_CUSTOM_COMPONENTS } from "./consts.js";
|
|
3
|
+
|
|
4
|
+
export class JSXUtils {
|
|
5
|
+
private static types: typeof t;
|
|
6
|
+
|
|
7
|
+
static init(types: typeof t) {
|
|
8
|
+
this.types = types;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static getAttributeName(attr: t.JSXAttribute): string {
|
|
12
|
+
return this.types.isJSXNamespacedName(attr.name)
|
|
13
|
+
? `${attr.name.namespace.name}:${attr.name.name.name}`
|
|
14
|
+
: attr.name.name;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static getElementName(node: t.JSXOpeningElement): string | null {
|
|
18
|
+
const name = node.name;
|
|
19
|
+
|
|
20
|
+
if (this.types.isJSXIdentifier(name)) {
|
|
21
|
+
return name.name;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (this.types.isJSXNamespacedName(name)) {
|
|
25
|
+
return `${name.namespace.name}:${name.name.name}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (this.types.isJSXMemberExpression(name)) {
|
|
29
|
+
return this.collectJSXMemberName(name);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private static collectJSXMemberName(
|
|
36
|
+
node: t.JSXMemberExpression
|
|
37
|
+
): string {
|
|
38
|
+
const parts: string[] = [node.property.name];
|
|
39
|
+
let current: t.JSXMemberExpression["object"] = node.object;
|
|
40
|
+
|
|
41
|
+
while (this.types.isJSXMemberExpression(current)) {
|
|
42
|
+
parts.unshift(current.property.name);
|
|
43
|
+
current = current.object;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (this.types.isJSXIdentifier(current)) {
|
|
47
|
+
parts.unshift(current.name);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return parts.join(".");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static isCustomComponent(name: string): boolean {
|
|
54
|
+
return name.length > 0 && name.charAt(0) === name.charAt(0).toUpperCase();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static isAllowedCustomComponent(name: string): boolean {
|
|
58
|
+
return ALLOWED_CUSTOM_COMPONENTS.includes(name);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static isDOMElement(name: string): boolean {
|
|
62
|
+
return name.length > 0 && name.charAt(0) === name.charAt(0).toLowerCase();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static isDOMOrAllowed(name: string): boolean {
|
|
66
|
+
return this.isDOMElement(name) || this.isAllowedCustomComponent(name);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static producesJSX(node: t.Expression | t.Node): boolean {
|
|
70
|
+
const types = this.types;
|
|
71
|
+
|
|
72
|
+
if (types.isJSXElement(node) || types.isJSXFragment(node)) return true;
|
|
73
|
+
|
|
74
|
+
if (types.isConditionalExpression(node)) {
|
|
75
|
+
return (
|
|
76
|
+
this.producesJSX(node.consequent) || this.producesJSX(node.alternate)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (types.isLogicalExpression(node)) {
|
|
81
|
+
return this.producesJSX(node.left) || this.producesJSX(node.right);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (types.isCallExpression(node)) {
|
|
85
|
+
if (types.isMemberExpression(node.callee)) {
|
|
86
|
+
const prop = node.callee.property;
|
|
87
|
+
if (
|
|
88
|
+
types.isIdentifier(prop) &&
|
|
89
|
+
(prop.name === "map" || prop.name === "flatMap")
|
|
90
|
+
) {
|
|
91
|
+
const callback = node.arguments[0];
|
|
92
|
+
if (callback) return this.callbackProducesJSX(callback);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (types.isParenthesizedExpression(node)) {
|
|
98
|
+
return this.producesJSX(node.expression);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private static callbackProducesJSX(
|
|
105
|
+
callback: t.Expression | t.SpreadElement | t.ArgumentPlaceholder
|
|
106
|
+
): boolean {
|
|
107
|
+
const types = this.types;
|
|
108
|
+
|
|
109
|
+
if (types.isArrowFunctionExpression(callback)) {
|
|
110
|
+
if (types.isBlockStatement(callback.body)) {
|
|
111
|
+
return this.doesBlockReturnJSX(callback.body);
|
|
112
|
+
}
|
|
113
|
+
return this.producesJSX(callback.body);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (types.isFunctionExpression(callback)) {
|
|
117
|
+
return this.doesBlockReturnJSX(callback.body);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static doesBlockReturnJSX(block: t.BlockStatement): boolean {
|
|
124
|
+
for (const stmt of block.body) {
|
|
125
|
+
if (this.types.isReturnStatement(stmt) && stmt.argument) {
|
|
126
|
+
if (this.producesJSX(stmt.argument)) return true;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import type { NodePath } from "@babel/traverse";
|
|
2
|
+
import type * as t from "@babel/types";
|
|
3
|
+
import type { CollectionInfo } from "../capabilities/inline-edit/types.js";
|
|
4
|
+
import {
|
|
5
|
+
DATA_COLLECTION_ID,
|
|
6
|
+
DATA_COLLECTION_REFERENCE,
|
|
7
|
+
MAX_JSX_DEPTH,
|
|
8
|
+
} from "../consts.js";
|
|
9
|
+
import { JSXUtils } from "../jsx-utils.js";
|
|
10
|
+
import {
|
|
11
|
+
JSXAttributeUtils,
|
|
12
|
+
PathNavigationUtils,
|
|
13
|
+
ExpressionAnalysisUtils,
|
|
14
|
+
} from "./utils/shared-utils.js";
|
|
15
|
+
import { CollectionTracingUtils } from "./utils/collection-tracing-utils.js";
|
|
16
|
+
|
|
17
|
+
export class CollectionIdProcessor {
|
|
18
|
+
private attributeUtils: JSXAttributeUtils;
|
|
19
|
+
private pathUtils: PathNavigationUtils;
|
|
20
|
+
private expressionUtils: ExpressionAnalysisUtils;
|
|
21
|
+
private tracingUtils: CollectionTracingUtils;
|
|
22
|
+
|
|
23
|
+
constructor(private types: typeof t) {
|
|
24
|
+
this.attributeUtils = new JSXAttributeUtils(types);
|
|
25
|
+
this.pathUtils = new PathNavigationUtils(types);
|
|
26
|
+
this.expressionUtils = new ExpressionAnalysisUtils(types);
|
|
27
|
+
this.tracingUtils = new CollectionTracingUtils(types);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
process(path: NodePath<t.JSXOpeningElement>): void {
|
|
31
|
+
if (this.attributeUtils.hasAttribute(path, DATA_COLLECTION_ID)) return;
|
|
32
|
+
|
|
33
|
+
const info =
|
|
34
|
+
this.tryDirectMapInChildren(path) ??
|
|
35
|
+
this.tryGetByIdInChildren(path) ??
|
|
36
|
+
this.tryComponentRootWithCollectionProp(path);
|
|
37
|
+
|
|
38
|
+
if (!info) return;
|
|
39
|
+
|
|
40
|
+
const target = this.pathUtils.findDOMElementTarget(path) ?? path;
|
|
41
|
+
|
|
42
|
+
this.attributeUtils.addStringAttribute(target, DATA_COLLECTION_ID, info.id);
|
|
43
|
+
|
|
44
|
+
if (info.references.length > 0) {
|
|
45
|
+
this.attributeUtils.addStringAttribute(
|
|
46
|
+
target,
|
|
47
|
+
DATA_COLLECTION_REFERENCE,
|
|
48
|
+
info.references.join(",")
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private tryDirectMapInChildren(
|
|
54
|
+
path: NodePath<t.JSXOpeningElement>
|
|
55
|
+
): CollectionInfo | null {
|
|
56
|
+
const jsxElement = path.parentPath;
|
|
57
|
+
if (!jsxElement?.isJSXElement()) return null;
|
|
58
|
+
|
|
59
|
+
const children = jsxElement.get("children");
|
|
60
|
+
for (const child of children) {
|
|
61
|
+
if (!child.isJSXExpressionContainer()) continue;
|
|
62
|
+
|
|
63
|
+
const expression = child.get("expression");
|
|
64
|
+
if (expression.isJSXEmptyExpression()) continue;
|
|
65
|
+
|
|
66
|
+
const mapSource = this.extractMapSource(
|
|
67
|
+
expression as NodePath<t.Expression>
|
|
68
|
+
);
|
|
69
|
+
if (mapSource) return mapSource;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private extractMapSource(
|
|
76
|
+
expr: NodePath<t.Expression>
|
|
77
|
+
): CollectionInfo | null {
|
|
78
|
+
if (expr.isCallExpression()) {
|
|
79
|
+
return this.traceMapCall(expr);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (expr.isLogicalExpression()) {
|
|
83
|
+
if (expr.node.operator === "&&") {
|
|
84
|
+
const right = expr.get("right") as NodePath<t.Expression>;
|
|
85
|
+
return this.extractMapSource(right);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (expr.isConditionalExpression()) {
|
|
90
|
+
const consequent = expr.get("consequent") as NodePath<t.Expression>;
|
|
91
|
+
const alternate = expr.get("alternate") as NodePath<t.Expression>;
|
|
92
|
+
return (
|
|
93
|
+
this.extractMapSource(consequent) ?? this.extractMapSource(alternate)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (expr.isOptionalCallExpression()) {
|
|
98
|
+
return this.traceOptionalMapCall(expr);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private traceMapCall(
|
|
105
|
+
callPath: NodePath<t.CallExpression>
|
|
106
|
+
): CollectionInfo | null {
|
|
107
|
+
const callee = callPath.get("callee");
|
|
108
|
+
if (!callee.isMemberExpression()) return null;
|
|
109
|
+
|
|
110
|
+
const property = callee.get("property") as NodePath;
|
|
111
|
+
if (
|
|
112
|
+
!property.isIdentifier() ||
|
|
113
|
+
(property.node.name !== "map" && property.node.name !== "flatMap")
|
|
114
|
+
) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!this.callbackProducesJSX(callPath)) return null;
|
|
119
|
+
|
|
120
|
+
const arrayObj = callee.get("object") as NodePath<t.Expression>;
|
|
121
|
+
return this.tracingUtils.traceCollectionSource(arrayObj);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private traceOptionalMapCall(
|
|
125
|
+
callPath: NodePath<t.OptionalCallExpression>
|
|
126
|
+
): CollectionInfo | null {
|
|
127
|
+
const callee = callPath.get("callee");
|
|
128
|
+
if (
|
|
129
|
+
!callee.isMemberExpression() &&
|
|
130
|
+
!callee.isOptionalMemberExpression()
|
|
131
|
+
) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const property = (callee as NodePath<t.MemberExpression>).get(
|
|
136
|
+
"property"
|
|
137
|
+
) as NodePath;
|
|
138
|
+
if (
|
|
139
|
+
!property.isIdentifier() ||
|
|
140
|
+
(property.node.name !== "map" && property.node.name !== "flatMap")
|
|
141
|
+
) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const arrayObj = (callee as NodePath<t.MemberExpression>).get(
|
|
146
|
+
"object"
|
|
147
|
+
) as NodePath<t.Expression>;
|
|
148
|
+
return this.tracingUtils.traceCollectionSource(arrayObj);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private callbackProducesJSX(
|
|
152
|
+
callPath: NodePath<t.CallExpression>
|
|
153
|
+
): boolean {
|
|
154
|
+
const args = callPath.get("arguments");
|
|
155
|
+
const callback = args[0];
|
|
156
|
+
if (!callback) return false;
|
|
157
|
+
|
|
158
|
+
if (callback.isArrowFunctionExpression()) {
|
|
159
|
+
const body = callback.get("body");
|
|
160
|
+
if (body.isBlockStatement()) {
|
|
161
|
+
return JSXUtils.doesBlockReturnJSX(body.node);
|
|
162
|
+
}
|
|
163
|
+
return JSXUtils.producesJSX(body.node);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (callback.isFunctionExpression()) {
|
|
167
|
+
return JSXUtils.doesBlockReturnJSX(callback.node.body);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private tryGetByIdInChildren(
|
|
174
|
+
path: NodePath<t.JSXOpeningElement>,
|
|
175
|
+
depth: number = 0
|
|
176
|
+
): CollectionInfo | null {
|
|
177
|
+
if (depth > MAX_JSX_DEPTH) return null;
|
|
178
|
+
|
|
179
|
+
const jsxElement = path.parentPath;
|
|
180
|
+
if (!jsxElement?.isJSXElement()) return null;
|
|
181
|
+
|
|
182
|
+
const children = jsxElement.get("children");
|
|
183
|
+
for (const child of children) {
|
|
184
|
+
const info = this.checkChildForGetById(child, depth);
|
|
185
|
+
if (info) return info;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
for (const attr of path.node.attributes) {
|
|
189
|
+
if (
|
|
190
|
+
this.types.isJSXAttribute(attr) &&
|
|
191
|
+
attr.value &&
|
|
192
|
+
this.types.isJSXExpressionContainer(attr.value)
|
|
193
|
+
) {
|
|
194
|
+
const expr = attr.value.expression;
|
|
195
|
+
if (this.types.isMemberExpression(expr)) {
|
|
196
|
+
const info = this.tracingUtils.traceGetByIdSource(path);
|
|
197
|
+
if (info) return info;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private checkChildForGetById(
|
|
206
|
+
child: NodePath,
|
|
207
|
+
depth: number
|
|
208
|
+
): CollectionInfo | null {
|
|
209
|
+
if (child.isJSXExpressionContainer()) {
|
|
210
|
+
const expr = child.get("expression");
|
|
211
|
+
if (
|
|
212
|
+
expr.isMemberExpression() ||
|
|
213
|
+
expr.isOptionalMemberExpression()
|
|
214
|
+
) {
|
|
215
|
+
return this.tracingUtils.traceGetByIdSource(child);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (child.isJSXElement()) {
|
|
220
|
+
const childOpening = child.get("openingElement");
|
|
221
|
+
return this.tryGetByIdInChildren(childOpening, depth + 1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private tryComponentRootWithCollectionProp(
|
|
228
|
+
path: NodePath<t.JSXOpeningElement>
|
|
229
|
+
): CollectionInfo | null {
|
|
230
|
+
if (!this.pathUtils.isRootReturnElement(path)) return null;
|
|
231
|
+
|
|
232
|
+
const fn = this.pathUtils.findEnclosingFunction(path);
|
|
233
|
+
if (!fn) return null;
|
|
234
|
+
|
|
235
|
+
let info: CollectionInfo | null = null;
|
|
236
|
+
|
|
237
|
+
fn.traverse({
|
|
238
|
+
CallExpression: (callPath: NodePath<t.CallExpression>) => {
|
|
239
|
+
if (info) return;
|
|
240
|
+
|
|
241
|
+
const callee = callPath.get("callee");
|
|
242
|
+
if (!callee.isMemberExpression()) return;
|
|
243
|
+
|
|
244
|
+
const prop = callee.get("property") as NodePath;
|
|
245
|
+
if (
|
|
246
|
+
!prop.isIdentifier() ||
|
|
247
|
+
(prop.node.name !== "map" && prop.node.name !== "flatMap")
|
|
248
|
+
) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (!this.callbackProducesJSX(callPath)) return;
|
|
253
|
+
|
|
254
|
+
const arrayObj = callee.get("object") as NodePath<t.Expression>;
|
|
255
|
+
info = this.tracingUtils.traceCollectionSource(arrayObj);
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
return info;
|
|
260
|
+
}
|
|
261
|
+
}
|