@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,555 @@
|
|
|
1
|
+
import { objectProperty, stringLiteral } from "@babel/types";
|
|
2
|
+
import { primitiveFailure } from "../../diagnostics/index.js";
|
|
3
|
+
import { createJsxAttribute, objectPropertyKey, parseMutationSource, printMutationWithOperation, resolveOptionalJsxAttribute, resolveRequiredJsxAttribute, resolveRequiredJsxElement, valueToExpression, } from "../shared.js";
|
|
4
|
+
import { analyzeClassExpression, } from "./class-expression.js";
|
|
5
|
+
export function setJsxAttribute(params) {
|
|
6
|
+
const { source, locator, value } = params;
|
|
7
|
+
const parsed = parseMutationSource(source, "jsx-attribute");
|
|
8
|
+
if (!parsed.ok) {
|
|
9
|
+
return parsed.result;
|
|
10
|
+
}
|
|
11
|
+
const elementMatch = resolveRequiredJsxElement(parsed.ast, locator.element, source);
|
|
12
|
+
if (!elementMatch.ok) {
|
|
13
|
+
return elementMatch.result;
|
|
14
|
+
}
|
|
15
|
+
const attributes = elementMatch.match.path.node.openingElement.attributes;
|
|
16
|
+
// Check if target attribute is followed by any spread attribute
|
|
17
|
+
const targetIndex = attributes.findIndex((attribute) => attribute.type === "JSXAttribute" &&
|
|
18
|
+
attribute.name.type === "JSXIdentifier" &&
|
|
19
|
+
attribute.name.name === locator.name);
|
|
20
|
+
if (targetIndex >= 0) {
|
|
21
|
+
const hasSpreadAfter = attributes.some((attribute, index) => index > targetIndex && attribute.type === "JSXSpreadAttribute");
|
|
22
|
+
if (hasSpreadAfter) {
|
|
23
|
+
return primitiveFailure(source, "unsupported-syntax", "cannot edit explicit attribute when spread props follow it", "jsx-attribute");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const existing = attributes.find((attribute) => attribute.type === "JSXAttribute" &&
|
|
27
|
+
attribute.name.type === "JSXIdentifier" &&
|
|
28
|
+
attribute.name.name === locator.name);
|
|
29
|
+
if (existing && existing.type === "JSXAttribute") {
|
|
30
|
+
existing.value = createJsxAttribute(locator.name, value).value;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
attributes.push(createJsxAttribute(locator.name, value));
|
|
34
|
+
}
|
|
35
|
+
return printMutationWithOperation(parsed.ast, source, {
|
|
36
|
+
kind: "set-jsx-attribute",
|
|
37
|
+
target: "jsx-attribute",
|
|
38
|
+
inverse: "remove-jsx-attribute",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function removeJsxAttribute(params) {
|
|
42
|
+
const { source, locator } = params;
|
|
43
|
+
const parsed = parseMutationSource(source, "jsx-attribute");
|
|
44
|
+
if (!parsed.ok) {
|
|
45
|
+
return parsed.result;
|
|
46
|
+
}
|
|
47
|
+
const elementMatch = resolveRequiredJsxElement(parsed.ast, locator.element, source);
|
|
48
|
+
if (!elementMatch.ok) {
|
|
49
|
+
return elementMatch.result;
|
|
50
|
+
}
|
|
51
|
+
elementMatch.match.path.node.openingElement.attributes =
|
|
52
|
+
elementMatch.match.path.node.openingElement.attributes.filter((attribute) => attribute.type !== "JSXAttribute" ||
|
|
53
|
+
attribute.name.type !== "JSXIdentifier" ||
|
|
54
|
+
attribute.name.name !== locator.name);
|
|
55
|
+
return printMutationWithOperation(parsed.ast, source, {
|
|
56
|
+
kind: "remove-jsx-attribute",
|
|
57
|
+
target: "jsx-attribute",
|
|
58
|
+
inverse: "set-jsx-attribute",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export function updateClassName(params) {
|
|
62
|
+
const dynamicResult = updateDynamicClassName(params);
|
|
63
|
+
if (dynamicResult.handled) {
|
|
64
|
+
return dynamicResult.result;
|
|
65
|
+
}
|
|
66
|
+
return setJsxAttribute({
|
|
67
|
+
source: params.source,
|
|
68
|
+
locator: params.locator,
|
|
69
|
+
value: params.value,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export function addTailwindClass(params) {
|
|
73
|
+
/* node:coverage ignore start */
|
|
74
|
+
return mutateTailwindClassTokens(params.source, params.locator, (tokens) => {
|
|
75
|
+
tokens.add(params.token);
|
|
76
|
+
}, "first");
|
|
77
|
+
/* node:coverage ignore stop */
|
|
78
|
+
}
|
|
79
|
+
export function removeTailwindClass(params) {
|
|
80
|
+
/* node:coverage ignore start */
|
|
81
|
+
return mutateTailwindClassTokens(params.source, params.locator, (tokens) => {
|
|
82
|
+
tokens.delete(params.token);
|
|
83
|
+
}, "all");
|
|
84
|
+
/* node:coverage ignore stop */
|
|
85
|
+
}
|
|
86
|
+
export function updateStyleProperty(params) {
|
|
87
|
+
const { source, locator, property, value } = params;
|
|
88
|
+
const parsed = parseMutationSource(source, "jsx-attribute");
|
|
89
|
+
if (!parsed.ok) {
|
|
90
|
+
return parsed.result;
|
|
91
|
+
}
|
|
92
|
+
const attributeMatch = resolveStyleAttributeForUpdate(parsed.ast, locator, source);
|
|
93
|
+
if (!attributeMatch.ok) {
|
|
94
|
+
return attributeMatch.result;
|
|
95
|
+
}
|
|
96
|
+
const attributePath = attributeMatch.path;
|
|
97
|
+
const attributeValue = attributePath.get("value");
|
|
98
|
+
if (!attributeValue.isJSXExpressionContainer()) {
|
|
99
|
+
return primitiveFailure(source, "unsupported-syntax", "style attribute must use JSX expression container", "jsx-attribute");
|
|
100
|
+
}
|
|
101
|
+
const expression = attributeValue.get("expression");
|
|
102
|
+
if (!expression.isObjectExpression()) {
|
|
103
|
+
return primitiveFailure(source, "unsupported-syntax", "style attribute must contain object literal", "jsx-attribute");
|
|
104
|
+
}
|
|
105
|
+
if (expression.node.properties.some((item) => item.type !== "ObjectProperty")) {
|
|
106
|
+
return primitiveFailure(source, "unsupported-syntax", "style object cannot contain spread properties", "jsx-attribute");
|
|
107
|
+
}
|
|
108
|
+
setStyleObjectProperty(expression, property, value);
|
|
109
|
+
return printMutationWithOperation(parsed.ast, source, {
|
|
110
|
+
kind: "update-style-property",
|
|
111
|
+
target: "jsx-attribute",
|
|
112
|
+
inverse: "remove-style-property",
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
export function updateStyleProperties(params) {
|
|
116
|
+
const { source, locator, properties } = params;
|
|
117
|
+
const parsed = parseMutationSource(source, "jsx-attribute");
|
|
118
|
+
if (!parsed.ok) {
|
|
119
|
+
return parsed.result;
|
|
120
|
+
}
|
|
121
|
+
const attributeMatch = resolveStyleAttributeForUpdate(parsed.ast, locator, source);
|
|
122
|
+
if (!attributeMatch.ok) {
|
|
123
|
+
return attributeMatch.result;
|
|
124
|
+
}
|
|
125
|
+
const attributePath = attributeMatch.path;
|
|
126
|
+
const attributeValue = attributePath.get("value");
|
|
127
|
+
if (!attributeValue.isJSXExpressionContainer()) {
|
|
128
|
+
return primitiveFailure(source, "unsupported-syntax", "style attribute must use JSX expression container", "jsx-attribute");
|
|
129
|
+
}
|
|
130
|
+
const expression = attributeValue.get("expression");
|
|
131
|
+
if (!expression.isObjectExpression()) {
|
|
132
|
+
return primitiveFailure(source, "unsupported-syntax", "style attribute must contain object literal", "jsx-attribute");
|
|
133
|
+
}
|
|
134
|
+
if (expression.node.properties.some((item) => item.type !== "ObjectProperty")) {
|
|
135
|
+
return primitiveFailure(source, "unsupported-syntax", "style object cannot contain spread properties", "jsx-attribute");
|
|
136
|
+
}
|
|
137
|
+
for (const [property, value] of Object.entries(properties)) {
|
|
138
|
+
setStyleObjectProperty(expression, property, value);
|
|
139
|
+
}
|
|
140
|
+
return printMutationWithOperation(parsed.ast, source, {
|
|
141
|
+
kind: "update-style-properties",
|
|
142
|
+
target: "jsx-attribute",
|
|
143
|
+
inverse: "remove-style-properties",
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function resolveStyleAttributeForUpdate(ast, locator, source) {
|
|
147
|
+
const attributeMatch = resolveOptionalJsxAttribute(ast, locator);
|
|
148
|
+
if (!attributeMatch.ok) {
|
|
149
|
+
return {
|
|
150
|
+
ok: false,
|
|
151
|
+
result: {
|
|
152
|
+
ok: false,
|
|
153
|
+
source,
|
|
154
|
+
diagnostic: attributeMatch.diagnostic,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (attributeMatch.match) {
|
|
159
|
+
if (!attributeMatch.match.path.isJSXAttribute()) {
|
|
160
|
+
return {
|
|
161
|
+
ok: false,
|
|
162
|
+
result: primitiveFailure(source, "unsupported-syntax", "JSX locator must resolve to JSXAttribute", "jsx-attribute"),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
ok: true,
|
|
167
|
+
path: attributeMatch.match.path,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const elementMatch = resolveRequiredJsxElement(ast, locator.element, source);
|
|
171
|
+
if (!elementMatch.ok) {
|
|
172
|
+
return elementMatch;
|
|
173
|
+
}
|
|
174
|
+
const openingElementPath = elementMatch.match.path.get("openingElement");
|
|
175
|
+
openingElementPath.node.attributes.push(createJsxAttribute(locator.name, {}));
|
|
176
|
+
const attributes = openingElementPath.get("attributes");
|
|
177
|
+
const created = attributes[attributes.length - 1];
|
|
178
|
+
if (!created?.isJSXAttribute()) {
|
|
179
|
+
return {
|
|
180
|
+
ok: false,
|
|
181
|
+
result: primitiveFailure(source, "unsupported-syntax", "Failed to create JSX style attribute", "jsx-attribute"),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return { ok: true, path: created };
|
|
185
|
+
}
|
|
186
|
+
function setStyleObjectProperty(expression, property, value) {
|
|
187
|
+
const existing = expression
|
|
188
|
+
.get("properties")
|
|
189
|
+
.find((item) => item.isObjectProperty() &&
|
|
190
|
+
getStylePropertyKey(item.node.key) === property);
|
|
191
|
+
if (existing && existing.isObjectProperty()) {
|
|
192
|
+
existing.get("value").replaceWith(valueToExpression(value));
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
expression.node.properties.push(objectProperty(objectPropertyKey(property), valueToExpression(value)));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
export function updateCssModuleClassReference(params) {
|
|
199
|
+
const { source, locator, nextClass } = params;
|
|
200
|
+
const parsed = parseMutationSource(source, "jsx-attribute");
|
|
201
|
+
if (!parsed.ok) {
|
|
202
|
+
return parsed.result;
|
|
203
|
+
}
|
|
204
|
+
const attributeMatch = resolveRequiredJsxAttribute(parsed.ast, locator, source);
|
|
205
|
+
if (!attributeMatch.ok) {
|
|
206
|
+
return attributeMatch.result;
|
|
207
|
+
}
|
|
208
|
+
const attributePath = attributeMatch.match.path;
|
|
209
|
+
const valuePath = attributePath.get("value");
|
|
210
|
+
if (!valuePath.isJSXExpressionContainer()) {
|
|
211
|
+
return primitiveFailure(source, "unsupported-syntax", "CSS module className must use JSX expression container", "jsx-attribute");
|
|
212
|
+
}
|
|
213
|
+
const expression = valuePath.get("expression");
|
|
214
|
+
if (!expression.isMemberExpression() ||
|
|
215
|
+
expression.node.object.type !== "Identifier" ||
|
|
216
|
+
expression.node.object.name !== "styles") {
|
|
217
|
+
return primitiveFailure(source, "unsupported-syntax", "CSS module className must be styles.foo or styles['foo']", "jsx-attribute");
|
|
218
|
+
}
|
|
219
|
+
if (expression.node.computed) {
|
|
220
|
+
if (expression.node.property.type !== "StringLiteral") {
|
|
221
|
+
return primitiveFailure(source, "unsupported-syntax", "CSS module computed access must use string literal key", "jsx-attribute");
|
|
222
|
+
}
|
|
223
|
+
expression.node.property.value = nextClass;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
if (expression.node.property.type !== "Identifier") {
|
|
227
|
+
return primitiveFailure(source, "unsupported-syntax", "CSS module property access must use identifier key", "jsx-attribute");
|
|
228
|
+
}
|
|
229
|
+
expression.node.property.name = nextClass;
|
|
230
|
+
}
|
|
231
|
+
return printMutationWithOperation(parsed.ast, source, {
|
|
232
|
+
kind: "update-css-module-class",
|
|
233
|
+
target: "jsx-attribute",
|
|
234
|
+
inverse: "restore-css-module-class",
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function mutateTailwindClassTokens(source, locator, mutate, strategy) {
|
|
238
|
+
const parsed = parseMutationSource(source, "jsx-attribute");
|
|
239
|
+
if (!parsed.ok) {
|
|
240
|
+
return parsed.result;
|
|
241
|
+
}
|
|
242
|
+
const attributeMatch = resolveRequiredJsxAttribute(parsed.ast, locator, source);
|
|
243
|
+
if (!attributeMatch.ok) {
|
|
244
|
+
return attributeMatch.result;
|
|
245
|
+
}
|
|
246
|
+
const currentValue = readStaticClassName(attributeMatch.match.path, source);
|
|
247
|
+
if (!currentValue.ok) {
|
|
248
|
+
const dynamicResult = mutateDynamicClassNameTokens(attributeMatch.match.path, source, mutate, strategy);
|
|
249
|
+
if (!dynamicResult.ok) {
|
|
250
|
+
return dynamicResult.result;
|
|
251
|
+
}
|
|
252
|
+
return printMutationWithOperation(parsed.ast, source, {
|
|
253
|
+
kind: "mutate-tailwind-class",
|
|
254
|
+
target: "jsx-attribute",
|
|
255
|
+
inverse: "restore-tailwind-class",
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
const tokens = new Set(currentValue.value.split(/\s+/).filter(Boolean));
|
|
259
|
+
mutate(tokens);
|
|
260
|
+
attributeMatch.match.path.node.value = createJsxAttribute(locator.name, [...tokens].join(" ")).value;
|
|
261
|
+
return printMutationWithOperation(parsed.ast, source, {
|
|
262
|
+
kind: "mutate-tailwind-class",
|
|
263
|
+
target: "jsx-attribute",
|
|
264
|
+
inverse: "restore-tailwind-class",
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
function updateDynamicClassName(params) {
|
|
268
|
+
const { source, locator, value } = params;
|
|
269
|
+
const parsed = parseMutationSource(source, "jsx-attribute");
|
|
270
|
+
if (!parsed.ok) {
|
|
271
|
+
return { handled: true, result: parsed.result };
|
|
272
|
+
}
|
|
273
|
+
const attributeMatch = resolveRequiredJsxAttribute(parsed.ast, locator, source);
|
|
274
|
+
if (!attributeMatch.ok) {
|
|
275
|
+
return { handled: false };
|
|
276
|
+
}
|
|
277
|
+
const attributePath = attributeMatch.match.path;
|
|
278
|
+
const valuePath = attributePath.get("value");
|
|
279
|
+
if (!valuePath.isJSXExpressionContainer()) {
|
|
280
|
+
return { handled: false };
|
|
281
|
+
}
|
|
282
|
+
const expression = valuePath.get("expression");
|
|
283
|
+
if (expression.isStringLiteral()) {
|
|
284
|
+
return { handled: false };
|
|
285
|
+
}
|
|
286
|
+
if (hasSpreadAfterAttribute(attributePath)) {
|
|
287
|
+
return {
|
|
288
|
+
handled: true,
|
|
289
|
+
result: primitiveFailure(source, "unsupported-syntax", "cannot edit explicit attribute when spread props follow it", "jsx-attribute"),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const analysis = analyzeClassExpression(expression);
|
|
293
|
+
if (!analysis.ok) {
|
|
294
|
+
expression.replaceWith(stringLiteral(value));
|
|
295
|
+
return {
|
|
296
|
+
handled: true,
|
|
297
|
+
result: printMutationWithOperation(parsed.ast, source, {
|
|
298
|
+
kind: "update-class-name",
|
|
299
|
+
target: "jsx-attribute",
|
|
300
|
+
inverse: "restore-class-name",
|
|
301
|
+
}),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
if (analysis.mode === "class-map-reference") {
|
|
305
|
+
const classMapResult = mutateClassMapReference(expression, analysis, source, (valuePath) => {
|
|
306
|
+
valuePath.node.value = value;
|
|
307
|
+
});
|
|
308
|
+
if (!classMapResult.ok) {
|
|
309
|
+
return { handled: true, result: classMapResult.result };
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
handled: true,
|
|
313
|
+
result: printMutationWithOperation(parsed.ast, source, {
|
|
314
|
+
kind: "update-class-name",
|
|
315
|
+
target: "jsx-attribute",
|
|
316
|
+
inverse: "restore-class-name",
|
|
317
|
+
}),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
if (analysis.mode !== "call") {
|
|
321
|
+
return {
|
|
322
|
+
handled: true,
|
|
323
|
+
result: primitiveFailure(source, "unsupported-syntax", "className expression is not a supported class call", "jsx-attribute"),
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
if (!replaceFirstClassStringLiteral(expression, value)) {
|
|
327
|
+
return {
|
|
328
|
+
handled: true,
|
|
329
|
+
result: primitiveFailure(source, "unsupported-syntax", "className expression does not contain an editable string literal", "jsx-attribute"),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
handled: true,
|
|
334
|
+
result: printMutationWithOperation(parsed.ast, source, {
|
|
335
|
+
kind: "update-class-name",
|
|
336
|
+
target: "jsx-attribute",
|
|
337
|
+
inverse: "restore-class-name",
|
|
338
|
+
}),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function mutateDynamicClassNameTokens(attributePath, source, mutate, strategy) {
|
|
342
|
+
const valuePath = attributePath.get("value");
|
|
343
|
+
if (!valuePath.isJSXExpressionContainer()) {
|
|
344
|
+
return {
|
|
345
|
+
ok: false,
|
|
346
|
+
result: primitiveFailure(source, "unsupported-syntax", "Tailwind class editing only supports static className strings or class call expressions", "jsx-attribute"),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
const expression = valuePath.get("expression");
|
|
350
|
+
const analysis = analyzeClassExpression(expression);
|
|
351
|
+
if (!analysis.ok) {
|
|
352
|
+
if (expression.isTemplateLiteral()) {
|
|
353
|
+
if (!mutateTemplateLiteralTokens(expression, mutate, strategy)) {
|
|
354
|
+
return {
|
|
355
|
+
ok: false,
|
|
356
|
+
result: primitiveFailure(source, "unsupported-syntax", "Template literal className does not contain editable static class tokens", "jsx-attribute"),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return { ok: true };
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
ok: false,
|
|
363
|
+
result: diagnosticAsPrimitiveFailure(source, analysis.diagnostic),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
if (analysis.mode === "class-map-reference") {
|
|
367
|
+
return mutateClassMapReference(expression, analysis, source, (valuePath) => {
|
|
368
|
+
mutateStringLiteralTokens(valuePath, mutate);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
if (analysis.mode !== "call") {
|
|
372
|
+
return {
|
|
373
|
+
ok: false,
|
|
374
|
+
result: primitiveFailure(source, "unsupported-syntax", "Tailwind class editing only supports class call expressions at this stage", "jsx-attribute"),
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const changed = mutateClassStringLiterals(expression, mutate, strategy);
|
|
378
|
+
if (changed === 0) {
|
|
379
|
+
return {
|
|
380
|
+
ok: false,
|
|
381
|
+
result: primitiveFailure(source, "unsupported-syntax", "Class call expression does not contain editable string literal class tokens", "jsx-attribute"),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
return { ok: true };
|
|
385
|
+
}
|
|
386
|
+
function mutateClassStringLiterals(path, mutate, strategy) {
|
|
387
|
+
if (path.isStringLiteral()) {
|
|
388
|
+
mutateStringLiteralTokens(path, mutate);
|
|
389
|
+
return 1;
|
|
390
|
+
}
|
|
391
|
+
if (path.isConditionalExpression()) {
|
|
392
|
+
let changed = mutateClassStringLiterals(path.get("consequent"), mutate, strategy);
|
|
393
|
+
if (strategy === "first" && changed > 0)
|
|
394
|
+
return changed;
|
|
395
|
+
changed += mutateClassStringLiterals(path.get("alternate"), mutate, strategy);
|
|
396
|
+
return changed;
|
|
397
|
+
}
|
|
398
|
+
if (path.isCallExpression() && isSupportedClassCall(path.node)) {
|
|
399
|
+
let changed = 0;
|
|
400
|
+
for (const argument of path.get("arguments")) {
|
|
401
|
+
if (!argument.isExpression())
|
|
402
|
+
continue;
|
|
403
|
+
changed += mutateClassStringLiterals(argument, mutate, strategy);
|
|
404
|
+
if (strategy === "first" && changed > 0)
|
|
405
|
+
return changed;
|
|
406
|
+
}
|
|
407
|
+
return changed;
|
|
408
|
+
}
|
|
409
|
+
return 0;
|
|
410
|
+
}
|
|
411
|
+
function mutateTemplateLiteralTokens(path, mutate, strategy) {
|
|
412
|
+
let changed = false;
|
|
413
|
+
for (const quasi of path.node.quasis) {
|
|
414
|
+
const before = quasi.value.raw;
|
|
415
|
+
const tokens = new Set(before.split(/\s+/).filter(Boolean));
|
|
416
|
+
if (tokens.size === 0)
|
|
417
|
+
continue;
|
|
418
|
+
mutate(tokens);
|
|
419
|
+
const next = [...tokens].join(" ");
|
|
420
|
+
quasi.value.raw = next;
|
|
421
|
+
quasi.value.cooked = next;
|
|
422
|
+
changed = true;
|
|
423
|
+
if (strategy === "first")
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
return changed;
|
|
427
|
+
}
|
|
428
|
+
function mutateClassMapReference(path, analysis, source, mutate) {
|
|
429
|
+
const objectName = analysis.segments.find((segment) => segment.objectName)?.objectName;
|
|
430
|
+
const keys = [
|
|
431
|
+
...new Set(analysis.segments
|
|
432
|
+
.map((segment) => segment.key)
|
|
433
|
+
.filter((key) => Boolean(key))),
|
|
434
|
+
];
|
|
435
|
+
if (!objectName || keys.length === 0) {
|
|
436
|
+
return {
|
|
437
|
+
ok: false,
|
|
438
|
+
result: primitiveFailure(source, "unsupported-syntax", "Class map reference is missing a static object name or key", "jsx-attribute"),
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
const binding = path.scope.getBinding(objectName);
|
|
442
|
+
const bindingPath = binding?.path;
|
|
443
|
+
if (!bindingPath?.isVariableDeclarator() ||
|
|
444
|
+
bindingPath.parent.type !== "VariableDeclaration" ||
|
|
445
|
+
bindingPath.parent.kind !== "const") {
|
|
446
|
+
return {
|
|
447
|
+
ok: false,
|
|
448
|
+
result: primitiveFailure(source, "unsupported-syntax", "Class map must reference a local const object literal", "jsx-attribute"),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
const init = bindingPath.get("init");
|
|
452
|
+
if (!init?.isObjectExpression()) {
|
|
453
|
+
return {
|
|
454
|
+
ok: false,
|
|
455
|
+
result: primitiveFailure(source, "unsupported-syntax", "Class map binding must be an object literal", "jsx-attribute"),
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
for (const key of keys) {
|
|
459
|
+
const property = findClassMapStringProperty(init, key, source);
|
|
460
|
+
if (!property.ok) {
|
|
461
|
+
return property;
|
|
462
|
+
}
|
|
463
|
+
mutate(property.path);
|
|
464
|
+
}
|
|
465
|
+
return { ok: true };
|
|
466
|
+
}
|
|
467
|
+
function findClassMapStringProperty(objectPath, key, source) {
|
|
468
|
+
for (const property of objectPath.get("properties")) {
|
|
469
|
+
if (!property.isObjectProperty()) {
|
|
470
|
+
return {
|
|
471
|
+
ok: false,
|
|
472
|
+
result: primitiveFailure(source, "unsupported-syntax", "Class map object cannot contain spread properties or methods", "jsx-attribute"),
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
if (getStylePropertyKey(property.node.key) !== key)
|
|
476
|
+
continue;
|
|
477
|
+
const value = property.get("value");
|
|
478
|
+
if (!value.isStringLiteral()) {
|
|
479
|
+
return {
|
|
480
|
+
ok: false,
|
|
481
|
+
result: primitiveFailure(source, "unsupported-syntax", `Class map key "${key}" must resolve to a string literal`, "jsx-attribute"),
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
return { ok: true, path: value };
|
|
485
|
+
}
|
|
486
|
+
return {
|
|
487
|
+
ok: false,
|
|
488
|
+
result: primitiveFailure(source, "unsupported-syntax", `Class map key "${key}" was not found`, "jsx-attribute"),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function replaceFirstClassStringLiteral(path, value) {
|
|
492
|
+
if (path.isStringLiteral()) {
|
|
493
|
+
path.node.value = value;
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
if (path.isConditionalExpression()) {
|
|
497
|
+
return (replaceFirstClassStringLiteral(path.get("consequent"), value) ||
|
|
498
|
+
replaceFirstClassStringLiteral(path.get("alternate"), value));
|
|
499
|
+
}
|
|
500
|
+
if (path.isCallExpression() && isSupportedClassCall(path.node)) {
|
|
501
|
+
for (const argument of path.get("arguments")) {
|
|
502
|
+
if (argument.isExpression() &&
|
|
503
|
+
replaceFirstClassStringLiteral(argument, value)) {
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
function mutateStringLiteralTokens(path, mutate) {
|
|
511
|
+
const tokens = new Set(path.node.value.split(/\s+/).filter(Boolean));
|
|
512
|
+
mutate(tokens);
|
|
513
|
+
path.node.value = [...tokens].join(" ");
|
|
514
|
+
}
|
|
515
|
+
function hasSpreadAfterAttribute(path) {
|
|
516
|
+
const openingElement = path.parentPath;
|
|
517
|
+
if (!openingElement?.isJSXOpeningElement())
|
|
518
|
+
return false;
|
|
519
|
+
const index = openingElement.node.attributes.indexOf(path.node);
|
|
520
|
+
return openingElement.node.attributes.some((attribute, attributeIndex) => attributeIndex > index && attribute.type === "JSXSpreadAttribute");
|
|
521
|
+
}
|
|
522
|
+
function isSupportedClassCall(node) {
|
|
523
|
+
return (node.callee.type === "Identifier" &&
|
|
524
|
+
(node.callee.name === "cn" ||
|
|
525
|
+
node.callee.name === "clsx" ||
|
|
526
|
+
node.callee.name === "twMerge"));
|
|
527
|
+
}
|
|
528
|
+
function diagnosticAsPrimitiveFailure(source, diagnostic) {
|
|
529
|
+
return primitiveFailure(source, diagnostic.code, diagnostic.message, diagnostic.locatorKind, diagnostic.details);
|
|
530
|
+
}
|
|
531
|
+
function readStaticClassName(path, source) {
|
|
532
|
+
const valuePath = path.get("value");
|
|
533
|
+
if (valuePath.isStringLiteral()) {
|
|
534
|
+
return { ok: true, value: valuePath.node.value };
|
|
535
|
+
}
|
|
536
|
+
if (valuePath.isJSXExpressionContainer()) {
|
|
537
|
+
const expression = valuePath.get("expression");
|
|
538
|
+
if (expression.isStringLiteral()) {
|
|
539
|
+
return { ok: true, value: expression.node.value };
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
ok: false,
|
|
544
|
+
result: primitiveFailure(source, "unsupported-syntax", "Tailwind class editing only supports static className strings", "jsx-attribute"),
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function getStylePropertyKey(key) {
|
|
548
|
+
if (key.type === "Identifier") {
|
|
549
|
+
return key.name;
|
|
550
|
+
}
|
|
551
|
+
if (key.type === "StringLiteral") {
|
|
552
|
+
return key.value;
|
|
553
|
+
}
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { updateValue, insertArrayElement, removeArrayElement, moveArrayElement, insertObjectProperty, removeObjectProperty, type UpdateValueParams, type InsertArrayElementParams, type RemoveArrayElementParams, type MoveArrayElementParams, type InsertObjectPropertyParams, type RemoveObjectPropertyParams, } from "./value-primitives.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { updateValue, insertArrayElement, removeArrayElement, moveArrayElement, insertObjectProperty, removeObjectProperty, } from "./value-primitives.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AstPrimitiveResult, PrimitiveValue, ValueLocator } from "../../types.js";
|
|
2
|
+
export interface UpdateValueParams {
|
|
3
|
+
source: string;
|
|
4
|
+
locator: ValueLocator;
|
|
5
|
+
next: PrimitiveValue;
|
|
6
|
+
}
|
|
7
|
+
export interface InsertArrayElementParams {
|
|
8
|
+
source: string;
|
|
9
|
+
locator: ValueLocator;
|
|
10
|
+
index: number;
|
|
11
|
+
value: PrimitiveValue;
|
|
12
|
+
}
|
|
13
|
+
export interface RemoveArrayElementParams {
|
|
14
|
+
source: string;
|
|
15
|
+
locator: ValueLocator;
|
|
16
|
+
index: number;
|
|
17
|
+
}
|
|
18
|
+
export interface MoveArrayElementParams {
|
|
19
|
+
source: string;
|
|
20
|
+
locator: ValueLocator;
|
|
21
|
+
fromIndex: number;
|
|
22
|
+
toIndex: number;
|
|
23
|
+
}
|
|
24
|
+
export interface InsertObjectPropertyParams {
|
|
25
|
+
source: string;
|
|
26
|
+
locator: ValueLocator;
|
|
27
|
+
property: {
|
|
28
|
+
key: string;
|
|
29
|
+
value: PrimitiveValue;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export interface RemoveObjectPropertyParams {
|
|
33
|
+
source: string;
|
|
34
|
+
locator: ValueLocator;
|
|
35
|
+
key: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function updateValue(params: UpdateValueParams): AstPrimitiveResult;
|
|
38
|
+
export declare function insertArrayElement(params: InsertArrayElementParams): AstPrimitiveResult;
|
|
39
|
+
export declare function removeArrayElement(params: RemoveArrayElementParams): AstPrimitiveResult;
|
|
40
|
+
export declare function moveArrayElement(params: MoveArrayElementParams): AstPrimitiveResult;
|
|
41
|
+
export declare function insertObjectProperty(params: InsertObjectPropertyParams): AstPrimitiveResult;
|
|
42
|
+
export declare function removeObjectProperty(params: RemoveObjectPropertyParams): AstPrimitiveResult;
|