@trojanbox-vcp-test/site-edit-engine 0.1.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 +85 -0
- package/dist/execute-integration/execute-fixture-harness.d.ts +25 -0
- package/dist/execute-integration/execute-fixture-harness.js +37 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/internal/ast/diagnostics/index.d.ts +5 -0
- package/dist/internal/ast/diagnostics/index.js +25 -0
- package/dist/internal/ast/history/index.d.ts +15 -0
- package/dist/internal/ast/history/index.js +62 -0
- package/dist/internal/ast/index.d.ts +8 -0
- package/dist/internal/ast/index.js +5 -0
- package/dist/internal/ast/locators/index.d.ts +1 -0
- package/dist/internal/ast/locators/index.js +1 -0
- package/dist/internal/ast/locators/resolve-locator.d.ts +16 -0
- package/dist/internal/ast/locators/resolve-locator.js +920 -0
- package/dist/internal/ast/parser/SourceParser.d.ts +30 -0
- package/dist/internal/ast/parser/SourceParser.js +49 -0
- package/dist/internal/ast/parser/index.d.ts +21 -0
- package/dist/internal/ast/parser/index.js +64 -0
- package/dist/internal/ast/primitives/conditional/conditional-primitives.d.ts +18 -0
- package/dist/internal/ast/primitives/conditional/conditional-primitives.js +237 -0
- package/dist/internal/ast/primitives/conditional/index.d.ts +1 -0
- package/dist/internal/ast/primitives/conditional/index.js +1 -0
- package/dist/internal/ast/primitives/imports/add-import.d.ts +18 -0
- package/dist/internal/ast/primitives/imports/add-import.js +111 -0
- package/dist/internal/ast/primitives/imports/index.d.ts +2 -0
- package/dist/internal/ast/primitives/imports/index.js +2 -0
- package/dist/internal/ast/primitives/imports/remove-import.d.ts +15 -0
- package/dist/internal/ast/primitives/imports/remove-import.js +72 -0
- package/dist/internal/ast/primitives/index.d.ts +10 -0
- package/dist/internal/ast/primitives/index.js +10 -0
- package/dist/internal/ast/primitives/jsx/index.d.ts +4 -0
- package/dist/internal/ast/primitives/jsx/index.js +4 -0
- package/dist/internal/ast/primitives/jsx/insert-child.d.ts +11 -0
- package/dist/internal/ast/primitives/jsx/insert-child.js +69 -0
- package/dist/internal/ast/primitives/jsx/move-node.d.ts +9 -0
- package/dist/internal/ast/primitives/jsx/move-node.js +76 -0
- package/dist/internal/ast/primitives/jsx/remove-node.d.ts +7 -0
- package/dist/internal/ast/primitives/jsx/remove-node.js +36 -0
- package/dist/internal/ast/primitives/jsx/update-text.d.ts +8 -0
- package/dist/internal/ast/primitives/jsx/update-text.js +81 -0
- package/dist/internal/ast/primitives/next/index.d.ts +1 -0
- package/dist/internal/ast/primitives/next/index.js +1 -0
- package/dist/internal/ast/primitives/next/next-primitives.d.ts +43 -0
- package/dist/internal/ast/primitives/next/next-primitives.js +211 -0
- package/dist/internal/ast/primitives/shared.d.ts +60 -0
- package/dist/internal/ast/primitives/shared.js +176 -0
- package/dist/internal/ast/primitives/style/class-expression.d.ts +23 -0
- package/dist/internal/ast/primitives/style/class-expression.js +174 -0
- package/dist/internal/ast/primitives/style/index.d.ts +1 -0
- package/dist/internal/ast/primitives/style/index.js +1 -0
- package/dist/internal/ast/primitives/style/style-primitives.d.ts +49 -0
- package/dist/internal/ast/primitives/style/style-primitives.js +555 -0
- package/dist/internal/ast/primitives/values/index.d.ts +1 -0
- package/dist/internal/ast/primitives/values/index.js +1 -0
- package/dist/internal/ast/primitives/values/value-primitives.d.ts +42 -0
- package/dist/internal/ast/primitives/values/value-primitives.js +158 -0
- package/dist/internal/ast/printer/SourcePrinter.d.ts +21 -0
- package/dist/internal/ast/printer/SourcePrinter.js +76 -0
- package/dist/internal/ast/printer/index.d.ts +6 -0
- package/dist/internal/ast/printer/index.js +126 -0
- package/dist/internal/ast/types.d.ts +190 -0
- package/dist/internal/ast/types.js +1 -0
- package/dist/internal/capability/capability-resolver.d.ts +16 -0
- package/dist/internal/capability/capability-resolver.js +127 -0
- package/dist/internal/classname-source.d.ts +24 -0
- package/dist/internal/classname-source.js +220 -0
- package/dist/internal/contracts/IEditEngineRuntime.d.ts +18 -0
- package/dist/internal/contracts/IEditEngineRuntime.js +1 -0
- package/dist/internal/domain/EditDiagnostic.d.ts +38 -0
- package/dist/internal/domain/EditDiagnostic.js +43 -0
- package/dist/internal/events/event-bus.d.ts +14 -0
- package/dist/internal/events/event-bus.js +21 -0
- package/dist/internal/graph/graph-builder.d.ts +12 -0
- package/dist/internal/graph/graph-builder.js +1371 -0
- package/dist/internal/graph/import-resolver.d.ts +31 -0
- package/dist/internal/graph/import-resolver.js +109 -0
- package/dist/internal/graph/project-graph-builder.d.ts +32 -0
- package/dist/internal/graph/project-graph-builder.js +133 -0
- package/dist/internal/graph/types.d.ts +114 -0
- package/dist/internal/graph/types.js +6 -0
- package/dist/internal/history/undo-redo.d.ts +28 -0
- package/dist/internal/history/undo-redo.js +42 -0
- package/dist/internal/index.d.ts +2 -0
- package/dist/internal/index.js +1 -0
- package/dist/internal/planner/planner.d.ts +104 -0
- package/dist/internal/planner/planner.js +2533 -0
- package/dist/internal/planner/types.d.ts +275 -0
- package/dist/internal/planner/types.js +6 -0
- package/dist/internal/protocol/boundary.d.ts +10 -0
- package/dist/internal/protocol/boundary.js +3 -0
- package/dist/internal/protocol/capability.d.ts +47 -0
- package/dist/internal/protocol/capability.js +8 -0
- package/dist/internal/protocol/error.d.ts +43 -0
- package/dist/internal/protocol/error.js +38 -0
- package/dist/internal/protocol/event.d.ts +39 -0
- package/dist/internal/protocol/event.js +3 -0
- package/dist/internal/protocol/identity.d.ts +26 -0
- package/dist/internal/protocol/identity.js +30 -0
- package/dist/internal/protocol/operation.d.ts +224 -0
- package/dist/internal/protocol/operation.js +8 -0
- package/dist/internal/protocol/render.d.ts +212 -0
- package/dist/internal/protocol/render.js +3 -0
- package/dist/internal/protocol.d.ts +9 -0
- package/dist/internal/protocol.js +2 -0
- package/dist/internal/provenance/binding-graph.d.ts +39 -0
- package/dist/internal/provenance/binding-graph.js +184 -0
- package/dist/internal/provenance/capability-policy.d.ts +15 -0
- package/dist/internal/provenance/capability-policy.js +96 -0
- package/dist/internal/provenance/data-source-classifier.d.ts +14 -0
- package/dist/internal/provenance/data-source-classifier.js +281 -0
- package/dist/internal/provenance/resolve-text-provenance.d.ts +45 -0
- package/dist/internal/provenance/resolve-text-provenance.js +3090 -0
- package/dist/internal/provenance/types.d.ts +89 -0
- package/dist/internal/provenance/types.js +1 -0
- package/dist/internal/render/component-semantic.d.ts +11 -0
- package/dist/internal/render/component-semantic.js +141 -0
- package/dist/internal/render/content-model.d.ts +3 -0
- package/dist/internal/render/content-model.js +89 -0
- package/dist/internal/render/media-model.d.ts +3 -0
- package/dist/internal/render/media-model.js +45 -0
- package/dist/internal/render/provenance-types.d.ts +33 -0
- package/dist/internal/render/provenance-types.js +1 -0
- package/dist/internal/render/render-projection.d.ts +24 -0
- package/dist/internal/render/render-projection.js +281 -0
- package/dist/internal/render/tailwind-style-model.d.ts +19 -0
- package/dist/internal/render/tailwind-style-model.js +1187 -0
- package/dist/internal/runtime/EditEngineRuntime.d.ts +25 -0
- package/dist/internal/runtime/EditEngineRuntime.js +89 -0
- package/dist/internal/runtime/EditEngineRuntimeSnapshot.d.ts +31 -0
- package/dist/internal/runtime/EditEngineRuntimeSnapshot.js +15 -0
- package/dist/internal/runtime/InternalEditEngine.d.ts +44 -0
- package/dist/internal/runtime/InternalEditEngine.js +1391 -0
- package/dist/internal/runtime.d.ts +3 -0
- package/dist/internal/runtime.js +1 -0
- package/dist/internal/topology/topology.d.ts +6 -0
- package/dist/internal/topology/topology.js +98 -0
- package/dist/internal/topology/types.d.ts +35 -0
- package/dist/internal/topology/types.js +5 -0
- package/dist/internal/types.d.ts +1 -0
- package/dist/internal/types.js +1 -0
- package/dist/internal/writeback/in-memory-fs.d.ts +7 -0
- package/dist/internal/writeback/in-memory-fs.js +44 -0
- package/dist/internal/writeback/types.d.ts +45 -0
- package/dist/internal/writeback/types.js +7 -0
- package/dist/internal/writeback/writeback-service.d.ts +7 -0
- package/dist/internal/writeback/writeback-service.js +568 -0
- package/dist/internal-adapter.d.ts +18 -0
- package/dist/internal-adapter.js +350 -0
- package/dist/next-app-router-fs.d.ts +2 -0
- package/dist/next-app-router-fs.js +64 -0
- package/dist/next-app-router.d.ts +11 -0
- package/dist/next-app-router.js +140 -0
- package/dist/preview-runtime.d.ts +394 -0
- package/dist/preview-runtime.js +102 -0
- package/dist/public-file-system.d.ts +7 -0
- package/dist/public-file-system.js +1 -0
- package/dist/runtime-sync.d.ts +95 -0
- package/dist/runtime-sync.js +321 -0
- package/dist/runtime.d.ts +340 -0
- package/dist/runtime.js +134 -0
- package/dist/site-edit-instrumentation.d.ts +19 -0
- package/dist/site-edit-instrumentation.js +322 -0
- package/dist/snapshot-file-system.d.ts +19 -0
- package/dist/snapshot-file-system.js +49 -0
- package/dist/source-watcher.d.ts +20 -0
- package/dist/source-watcher.js +150 -0
- package/dist/source-writeback-test-harness.d.ts +244 -0
- package/dist/source-writeback-test-harness.js +119 -0
- package/dist/types.d.ts +68 -0
- package/dist/types.js +1 -0
- package/dist/webpack-loader.cjs +592 -0
- package/dist/webpack-loader.d.ts +27 -0
- package/package.json +66 -0
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
const generateModule = require("@babel/generator");
|
|
2
|
+
const parser = require("@babel/parser");
|
|
3
|
+
const traverseModule = require("@babel/traverse");
|
|
4
|
+
const t = require("@babel/types");
|
|
5
|
+
|
|
6
|
+
const generate =
|
|
7
|
+
typeof generateModule === "function"
|
|
8
|
+
? generateModule
|
|
9
|
+
: generateModule.default;
|
|
10
|
+
const traverse =
|
|
11
|
+
typeof traverseModule === "function"
|
|
12
|
+
? traverseModule
|
|
13
|
+
: traverseModule.default;
|
|
14
|
+
|
|
15
|
+
const ATTRIBUTES = {
|
|
16
|
+
key: "data-vcp-edit-key",
|
|
17
|
+
parentKey: "data-vcp-edit-parent-key",
|
|
18
|
+
movable: "data-vcp-movable",
|
|
19
|
+
removable: "data-vcp-removable",
|
|
20
|
+
flags: "data-vcp-flags",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const NON_EDITABLE_INTRINSIC_ELEMENTS = new Set([
|
|
24
|
+
"base",
|
|
25
|
+
"body",
|
|
26
|
+
"head",
|
|
27
|
+
"html",
|
|
28
|
+
"link",
|
|
29
|
+
"meta",
|
|
30
|
+
"noscript",
|
|
31
|
+
"script",
|
|
32
|
+
"style",
|
|
33
|
+
"template",
|
|
34
|
+
"title",
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const CONTAINER_INTRINSIC_ELEMENTS = new Set([
|
|
38
|
+
"article",
|
|
39
|
+
"aside",
|
|
40
|
+
"div",
|
|
41
|
+
"footer",
|
|
42
|
+
"form",
|
|
43
|
+
"header",
|
|
44
|
+
"li",
|
|
45
|
+
"main",
|
|
46
|
+
"nav",
|
|
47
|
+
"ol",
|
|
48
|
+
"section",
|
|
49
|
+
"ul",
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
const SUPPRESS_HYDRATION_WARNING_PROP = "suppressHydrationWarning";
|
|
53
|
+
|
|
54
|
+
function siteEditInstrumentationLoader(source, inputSourceMap) {
|
|
55
|
+
this.cacheable?.();
|
|
56
|
+
const callback = this.async();
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
if (!shouldInstrumentResource(this.resourcePath)) {
|
|
60
|
+
callback(null, source, inputSourceMap);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const options = this.getOptions?.() ?? {};
|
|
65
|
+
const result = instrumentSiteEditRuntimeAttributes({
|
|
66
|
+
source,
|
|
67
|
+
file: this.resourcePath,
|
|
68
|
+
projectRoot: options.projectRoot,
|
|
69
|
+
});
|
|
70
|
+
callback(null, result.code, inputSourceMap);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
callback(error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function shouldInstrumentResource(resourcePath) {
|
|
77
|
+
return (
|
|
78
|
+
typeof resourcePath === "string" &&
|
|
79
|
+
/\.(jsx|tsx)$/.test(resourcePath) &&
|
|
80
|
+
!resourcePath.includes("/node_modules/") &&
|
|
81
|
+
!resourcePath.endsWith(".d.ts")
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function instrumentSiteEditRuntimeAttributes(options) {
|
|
86
|
+
if (!mayContainJsx(options.source)) {
|
|
87
|
+
return { code: options.source, elements: [] };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const attributes = options.attributes ?? ATTRIBUTES;
|
|
91
|
+
const ast = parser.parse(options.source, {
|
|
92
|
+
sourceType: "module",
|
|
93
|
+
plugins: ["jsx", "typescript"],
|
|
94
|
+
});
|
|
95
|
+
const elements = [];
|
|
96
|
+
const sourceFile = toProjectRelativeFile(options.file, options.projectRoot);
|
|
97
|
+
|
|
98
|
+
traverse(ast, {
|
|
99
|
+
JSXElement(path) {
|
|
100
|
+
const opening = path.node.openingElement;
|
|
101
|
+
const name = opening.name;
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
!isSiteEditMarkableElement(name) ||
|
|
105
|
+
hasAttribute(opening, attributes.key)
|
|
106
|
+
) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const componentName = findOwningComponentName(path) ?? "default";
|
|
111
|
+
const structuralPath = computeStructuralPathFromAst(path);
|
|
112
|
+
const key = computeSiteEditSourceIdentityKey(
|
|
113
|
+
sourceFile,
|
|
114
|
+
componentName,
|
|
115
|
+
structuralPath,
|
|
116
|
+
);
|
|
117
|
+
const tag = getJsxElementName(name) ?? "unknown";
|
|
118
|
+
const sourceKind = isConcreteElement(name) ? "jsx" : "component-call";
|
|
119
|
+
if (
|
|
120
|
+
sourceKind === "component-call" &&
|
|
121
|
+
!hasStructuredChild(path.node.children)
|
|
122
|
+
) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
appendStringAttribute(opening, attributes.key, key);
|
|
127
|
+
appendBooleanAttribute(opening, SUPPRESS_HYDRATION_WARNING_PROP);
|
|
128
|
+
|
|
129
|
+
elements.push({
|
|
130
|
+
key,
|
|
131
|
+
tag,
|
|
132
|
+
component_name: componentName,
|
|
133
|
+
source_file: sourceFile,
|
|
134
|
+
source_kind: sourceKind,
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
code: generate(ast, { jsescOption: { minimal: true } }).code,
|
|
141
|
+
elements,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function mayContainJsx(source) {
|
|
146
|
+
return source.includes("<");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function computeSiteEditSourceIdentityKey(file, component, structuralPath) {
|
|
150
|
+
return `${file}::${component}::${structuralPath}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function toProjectRelativeFile(filename, projectRoot) {
|
|
154
|
+
const normalized = filename.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
155
|
+
const normalizedRoot = projectRoot?.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
156
|
+
|
|
157
|
+
if (normalizedRoot && normalized.startsWith(`${normalizedRoot}/`)) {
|
|
158
|
+
return normalized.slice(normalizedRoot.length + 1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
for (const [marker, offset] of [
|
|
162
|
+
["/src/app/", 1],
|
|
163
|
+
["/src/components/", 1],
|
|
164
|
+
["/app/", 1],
|
|
165
|
+
["/components/", 1],
|
|
166
|
+
]) {
|
|
167
|
+
const index = normalized.indexOf(marker);
|
|
168
|
+
if (index >= 0) {
|
|
169
|
+
return normalized.slice(index + offset);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const fallback = normalized.match(
|
|
174
|
+
/(?:^|\/)((?:src\/app|src\/components|app|components)\/.+)$/,
|
|
175
|
+
);
|
|
176
|
+
return fallback?.[1] ?? normalized;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isConcreteElement(name) {
|
|
180
|
+
return t.isJSXIdentifier(name) && /^[a-z]/.test(name.name);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function getJsxElementName(name) {
|
|
184
|
+
if (t.isJSXIdentifier(name)) {
|
|
185
|
+
return name.name;
|
|
186
|
+
}
|
|
187
|
+
if (t.isJSXMemberExpression(name)) {
|
|
188
|
+
return getJsxMemberExpressionName(name);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function isSiteEditMarkableElement(name) {
|
|
195
|
+
const elementName = getJsxElementName(name);
|
|
196
|
+
if (
|
|
197
|
+
!elementName ||
|
|
198
|
+
elementName === "Fragment" ||
|
|
199
|
+
elementName.endsWith(".Fragment")
|
|
200
|
+
) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return !NON_EDITABLE_INTRINSIC_ELEMENTS.has(elementName);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function hasAttribute(opening, name) {
|
|
208
|
+
return opening.attributes.some(
|
|
209
|
+
(attribute) =>
|
|
210
|
+
t.isJSXAttribute(attribute) &&
|
|
211
|
+
t.isJSXIdentifier(attribute.name, { name }),
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function hasDirectTextChild(children) {
|
|
216
|
+
return children.some((child) => {
|
|
217
|
+
if (t.isJSXText(child)) {
|
|
218
|
+
return child.value.trim().length > 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!t.isJSXExpressionContainer(child)) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (t.isStringLiteral(child.expression)) {
|
|
226
|
+
return child.expression.value.trim().length > 0;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (t.isTemplateLiteral(child.expression)) {
|
|
230
|
+
return child.expression.quasis.some((quasi) =>
|
|
231
|
+
quasi.value.cooked?.trim(),
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return isPotentialDirectTextExpression(child.expression);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function isPotentialDirectTextExpression(expression) {
|
|
240
|
+
const unwrapped = unwrapTextExpression(expression);
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
t.isIdentifier(unwrapped) ||
|
|
244
|
+
t.isMemberExpression(unwrapped) ||
|
|
245
|
+
t.isBinaryExpression(unwrapped) ||
|
|
246
|
+
t.isLogicalExpression(unwrapped) ||
|
|
247
|
+
t.isConditionalExpression(unwrapped)
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function unwrapTextExpression(expression) {
|
|
252
|
+
let current = expression;
|
|
253
|
+
|
|
254
|
+
while (
|
|
255
|
+
t.isTSAsExpression(current) ||
|
|
256
|
+
t.isTSSatisfiesExpression(current) ||
|
|
257
|
+
t.isTSNonNullExpression(current) ||
|
|
258
|
+
t.isTypeCastExpression(current)
|
|
259
|
+
) {
|
|
260
|
+
current = current.expression;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return current;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function appendStringAttribute(opening, name, value) {
|
|
267
|
+
opening.attributes.push(
|
|
268
|
+
t.jsxAttribute(t.jsxIdentifier(name), t.stringLiteral(value)),
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function appendOptionalStringAttribute(opening, name, value) {
|
|
273
|
+
if (value === undefined || hasAttribute(opening, name)) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
appendStringAttribute(opening, name, value);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function appendBooleanAttribute(opening, name) {
|
|
281
|
+
if (hasAttribute(opening, name)) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
opening.attributes.push(t.jsxAttribute(t.jsxIdentifier(name), null));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function getJsxMemberExpressionName(name) {
|
|
289
|
+
const objectName = t.isJSXIdentifier(name.object)
|
|
290
|
+
? name.object.name
|
|
291
|
+
: getJsxMemberExpressionName(name.object);
|
|
292
|
+
|
|
293
|
+
return `${objectName}.${name.property.name}`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function findOwningComponentName(path) {
|
|
297
|
+
let current = path;
|
|
298
|
+
|
|
299
|
+
while (current) {
|
|
300
|
+
if (current.isFunctionDeclaration() || current.isClassDeclaration()) {
|
|
301
|
+
return current.node.id?.name ?? null;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (current.isVariableDeclarator() && t.isIdentifier(current.node.id)) {
|
|
305
|
+
const init = current.get("init");
|
|
306
|
+
if (
|
|
307
|
+
!Array.isArray(init) &&
|
|
308
|
+
(init.isArrowFunctionExpression() || init.isFunctionExpression())
|
|
309
|
+
) {
|
|
310
|
+
return current.node.id.name;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
current = current.parentPath;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function findParentSiteEditElement(path) {
|
|
321
|
+
let current = path.parentPath;
|
|
322
|
+
|
|
323
|
+
while (current) {
|
|
324
|
+
if (
|
|
325
|
+
current.isJSXElement() &&
|
|
326
|
+
isSiteEditMarkableElement(current.node.openingElement.name)
|
|
327
|
+
) {
|
|
328
|
+
return current;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
current = current.parentPath;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function resolveParentKey(path, componentName, sourceFile, attributes) {
|
|
338
|
+
const parentElement = findParentSiteEditElement(path);
|
|
339
|
+
if (!parentElement) {
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const explicitParentKey = getStringAttributeValue(
|
|
344
|
+
parentElement.node.openingElement,
|
|
345
|
+
attributes.key,
|
|
346
|
+
);
|
|
347
|
+
if (explicitParentKey) {
|
|
348
|
+
return explicitParentKey;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const parentStructuralPath = computeStructuralPathFromAst(parentElement);
|
|
352
|
+
return computeSiteEditSourceIdentityKey(
|
|
353
|
+
sourceFile,
|
|
354
|
+
componentName,
|
|
355
|
+
parentStructuralPath,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function getStringAttributeValue(opening, name) {
|
|
360
|
+
const attribute = opening.attributes.find(
|
|
361
|
+
(item) => t.isJSXAttribute(item) && t.isJSXIdentifier(item.name, { name }),
|
|
362
|
+
);
|
|
363
|
+
if (
|
|
364
|
+
!attribute ||
|
|
365
|
+
!t.isJSXAttribute(attribute) ||
|
|
366
|
+
!t.isStringLiteral(attribute.value)
|
|
367
|
+
) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return attribute.value.value;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function hasStructuredChild(children) {
|
|
375
|
+
return children.some(
|
|
376
|
+
(child) => t.isJSXElement(child) || t.isJSXFragment(child),
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const structuralPathCache = new WeakMap();
|
|
381
|
+
const structuralChildrenCache = new WeakMap();
|
|
382
|
+
const siblingOrdinalCache = new WeakMap();
|
|
383
|
+
|
|
384
|
+
function computeStructuralPathFromAst(path) {
|
|
385
|
+
const cached = structuralPathCache.get(path.node);
|
|
386
|
+
if (cached !== undefined) {
|
|
387
|
+
return cached;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const segments = [];
|
|
391
|
+
let current = path;
|
|
392
|
+
|
|
393
|
+
while (current) {
|
|
394
|
+
if (current.isJSXElement()) {
|
|
395
|
+
const segment = getStructuralSegment(current);
|
|
396
|
+
if (segment) {
|
|
397
|
+
segments.unshift(segment);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
current = current.parentPath;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const structuralPath = segments.join("/");
|
|
405
|
+
structuralPathCache.set(path.node, structuralPath);
|
|
406
|
+
return structuralPath;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function getStructuralSegment(path) {
|
|
410
|
+
const tag = getJsxElementName(path.node.openingElement.name);
|
|
411
|
+
if (!tag || tag === "Fragment" || tag.endsWith(".Fragment")) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (/^[A-Z]/.test(tag)) {
|
|
416
|
+
return `component:${tag}#${computeSiblingOrdinal(path, tag)}`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return `${tag}#${computeSiblingOrdinal(path, tag)}`;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function computeSiblingOrdinal(path, tag) {
|
|
423
|
+
const owner = findStructuralParent(path);
|
|
424
|
+
if (!owner) {
|
|
425
|
+
return 0;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const cached = siblingOrdinalCache.get(owner.node)?.get(tag)?.get(path.node);
|
|
429
|
+
if (cached !== undefined) {
|
|
430
|
+
return cached;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
let ownerCache = siblingOrdinalCache.get(owner.node);
|
|
434
|
+
if (!ownerCache) {
|
|
435
|
+
ownerCache = new Map();
|
|
436
|
+
siblingOrdinalCache.set(owner.node, ownerCache);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
let tagOrdinals = ownerCache.get(tag);
|
|
440
|
+
if (!tagOrdinals) {
|
|
441
|
+
tagOrdinals = new WeakMap();
|
|
442
|
+
ownerCache.set(tag, tagOrdinals);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
let ordinal = 0;
|
|
446
|
+
for (const sibling of collectStructuralChildren(owner)) {
|
|
447
|
+
const siblingTag = getJsxElementName(sibling.node.openingElement.name);
|
|
448
|
+
if (siblingTag === tag) {
|
|
449
|
+
tagOrdinals.set(sibling.node, ordinal);
|
|
450
|
+
if (sibling.node === path.node) {
|
|
451
|
+
return ordinal;
|
|
452
|
+
}
|
|
453
|
+
ordinal += 1;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return tagOrdinals.get(path.node) ?? 0;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function findStructuralParent(path) {
|
|
461
|
+
let current = path.parentPath;
|
|
462
|
+
let fragmentOwner = null;
|
|
463
|
+
|
|
464
|
+
while (current) {
|
|
465
|
+
if (current.isJSXFragment()) {
|
|
466
|
+
fragmentOwner = current;
|
|
467
|
+
current = current.parentPath;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (current.isJSXElement()) {
|
|
472
|
+
return current;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
current = current.parentPath;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return fragmentOwner;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function collectStructuralChildren(owner) {
|
|
482
|
+
const cached = structuralChildrenCache.get(owner.node);
|
|
483
|
+
if (cached) {
|
|
484
|
+
return cached;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const structuralChildren = [];
|
|
488
|
+
|
|
489
|
+
const children = owner.get("children");
|
|
490
|
+
for (const child of children) {
|
|
491
|
+
if (Array.isArray(child)) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (child.isJSXElement()) {
|
|
496
|
+
structuralChildren.push(child);
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (child.isJSXFragment()) {
|
|
501
|
+
structuralChildren.push(...collectStructuralChildren(child));
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (child.isJSXExpressionContainer()) {
|
|
506
|
+
structuralChildren.push(
|
|
507
|
+
...collectExpressionStructuralChildren(child.get("expression")),
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
structuralChildrenCache.set(owner.node, structuralChildren);
|
|
513
|
+
return structuralChildren;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function collectExpressionStructuralChildren(expression) {
|
|
517
|
+
if (expression.isJSXElement()) {
|
|
518
|
+
return [expression];
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (expression.isJSXFragment()) {
|
|
522
|
+
return collectStructuralChildren(expression);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (expression.isParenthesizedExpression()) {
|
|
526
|
+
return collectExpressionStructuralChildren(expression.get("expression"));
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (expression.isLogicalExpression() && expression.node.operator === "&&") {
|
|
530
|
+
return collectExpressionStructuralChildren(expression.get("right"));
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (expression.isConditionalExpression()) {
|
|
534
|
+
return [
|
|
535
|
+
...collectExpressionStructuralChildren(expression.get("consequent")),
|
|
536
|
+
...collectExpressionStructuralChildren(expression.get("alternate")),
|
|
537
|
+
];
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (expression.isCallExpression() && isMapCallExpression(expression.node)) {
|
|
541
|
+
return collectFunctionReturnStructuralChildren(
|
|
542
|
+
expression.get("arguments")[0],
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return [];
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function collectFunctionReturnStructuralChildren(path) {
|
|
550
|
+
if (!path?.isArrowFunctionExpression() && !path?.isFunctionExpression()) {
|
|
551
|
+
return [];
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const body = path.get("body");
|
|
555
|
+
if (body.isJSXElement()) {
|
|
556
|
+
return [body];
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (body.isJSXFragment()) {
|
|
560
|
+
return collectStructuralChildren(body);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (body.isParenthesizedExpression()) {
|
|
564
|
+
return collectExpressionStructuralChildren(body.get("expression"));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (!body.isBlockStatement()) {
|
|
568
|
+
return [];
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
for (const statement of body.get("body")) {
|
|
572
|
+
if (!statement.isReturnStatement()) {
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
const argument = statement.get("argument");
|
|
576
|
+
return argument.node ? collectExpressionStructuralChildren(argument) : [];
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
return [];
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function isMapCallExpression(node) {
|
|
583
|
+
return (
|
|
584
|
+
t.isMemberExpression(node.callee) &&
|
|
585
|
+
t.isIdentifier(node.callee.property) &&
|
|
586
|
+
node.callee.property.name === "map"
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
module.exports = siteEditInstrumentationLoader;
|
|
591
|
+
module.exports.instrumentSiteEditRuntimeAttributes =
|
|
592
|
+
instrumentSiteEditRuntimeAttributes;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SiteEditInstrumentationOptions,
|
|
3
|
+
SiteEditInstrumentationResult,
|
|
4
|
+
} from "./site-edit-instrumentation.js";
|
|
5
|
+
|
|
6
|
+
declare function siteEditInstrumentationLoader(
|
|
7
|
+
this: {
|
|
8
|
+
cacheable?: () => void;
|
|
9
|
+
async?: () => (
|
|
10
|
+
error?: Error | null,
|
|
11
|
+
code?: string,
|
|
12
|
+
inputSourceMap?: unknown,
|
|
13
|
+
) => void;
|
|
14
|
+
resourcePath?: string;
|
|
15
|
+
getOptions?: () => { projectRoot?: string };
|
|
16
|
+
},
|
|
17
|
+
source: string,
|
|
18
|
+
inputSourceMap?: unknown,
|
|
19
|
+
): void;
|
|
20
|
+
|
|
21
|
+
declare namespace siteEditInstrumentationLoader {
|
|
22
|
+
function instrumentSiteEditRuntimeAttributes(
|
|
23
|
+
options: SiteEditInstrumentationOptions,
|
|
24
|
+
): SiteEditInstrumentationResult;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export = siteEditInstrumentationLoader;
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@trojanbox-vcp-test/site-edit-engine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://gitlab.cedemo.cn/vibe-coding-platform/vcp.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://gitlab.cedemo.cn/vibe-coding-platform/vcp",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://gitlab.cedemo.cn/vibe-coding-platform/vcp/-/issues"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"development": "./src/index.ts",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./next-app-router": {
|
|
26
|
+
"development": "./src/next-app-router.ts",
|
|
27
|
+
"types": "./dist/next-app-router.d.ts",
|
|
28
|
+
"default": "./dist/next-app-router.js"
|
|
29
|
+
},
|
|
30
|
+
"./source-watcher": {
|
|
31
|
+
"development": "./src/source-watcher.ts",
|
|
32
|
+
"types": "./dist/source-watcher.d.ts",
|
|
33
|
+
"default": "./dist/source-watcher.js"
|
|
34
|
+
},
|
|
35
|
+
"./webpack-loader": {
|
|
36
|
+
"development": "./src/webpack-loader.cjs",
|
|
37
|
+
"types": "./dist/webpack-loader.d.ts",
|
|
38
|
+
"default": "./dist/webpack-loader.cjs"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"README.md"
|
|
44
|
+
],
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=24.0.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "pnpm --dir ../contracts build && node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs'); fs.mkdirSync('dist', { recursive: true }); fs.copyFileSync('src/webpack-loader.cjs', 'dist/webpack-loader.cjs'); fs.copyFileSync('src/webpack-loader.d.ts', 'dist/webpack-loader.d.ts')\"",
|
|
50
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@babel/generator": "^7.29.1",
|
|
54
|
+
"@babel/parser": "^7.29.3",
|
|
55
|
+
"@babel/traverse": "^7.29.0",
|
|
56
|
+
"@babel/types": "^7.29.0",
|
|
57
|
+
"@trojanbox-vcp-test/contracts": "^0.1.0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/babel__generator": "^7.27.0",
|
|
61
|
+
"@types/babel__traverse": "^7.28.0",
|
|
62
|
+
"@types/node": "^24.0.0",
|
|
63
|
+
"typescript": "^5.8.0",
|
|
64
|
+
"vitest": "^3.2.0"
|
|
65
|
+
}
|
|
66
|
+
}
|