100 lines
3.7 KiB
JavaScript
100 lines
3.7 KiB
JavaScript
import { parse } from "@babel/parser";
|
|
import { default as traverse } from "@babel/traverse";
|
|
import { default as generate } from "@babel/generator";
|
|
import * as t from "@babel/types";
|
|
import { JSXProcessor } from "./jsx-processor.js";
|
|
import { JSXUtils } from "./jsx-utils.js";
|
|
export function visualEditPlugin() {
|
|
return {
|
|
name: "visual-edit-transform",
|
|
apply: (config) => config.mode === "development",
|
|
enforce: "pre",
|
|
order: "pre",
|
|
transformIndexHtml(html) {
|
|
const tailwindScript = ` <!-- Tailwind CSS CDN for visual editing -->\n <script src="https://cdn.tailwindcss.com"></script>\n `;
|
|
return html.replace("</head>", tailwindScript + "</head>");
|
|
},
|
|
transform(code, id) {
|
|
if (id.includes("node_modules") || id.includes("visual-edit-agent")) {
|
|
return null;
|
|
}
|
|
if (!id.match(/\.(jsx?|tsx?)$/)) {
|
|
return null;
|
|
}
|
|
const filename = extractFilename(id);
|
|
try {
|
|
const ast = parse(code, {
|
|
sourceType: "module",
|
|
plugins: [
|
|
"jsx",
|
|
"typescript",
|
|
"decorators-legacy",
|
|
"classProperties",
|
|
"objectRestSpread",
|
|
"functionBind",
|
|
"exportDefaultFrom",
|
|
"exportNamespaceFrom",
|
|
"dynamicImport",
|
|
"nullishCoalescingOperator",
|
|
"optionalChaining",
|
|
"asyncGenerators",
|
|
"bigInt",
|
|
"optionalCatchBinding",
|
|
"throwExpressions",
|
|
],
|
|
});
|
|
JSXUtils.init(t);
|
|
const processor = new JSXProcessor(t, filename);
|
|
traverse.default(ast, {
|
|
JSXElement(path) {
|
|
const jsxElement = path.node;
|
|
if (t.isJSXFragment(jsxElement))
|
|
return;
|
|
processor.processJSXElement(path.get("openingElement"));
|
|
},
|
|
});
|
|
const result = generate.default(ast, {
|
|
compact: false,
|
|
concise: false,
|
|
retainLines: true,
|
|
});
|
|
return {
|
|
code: result.code,
|
|
map: null,
|
|
};
|
|
}
|
|
catch (error) {
|
|
console.error("Failed to add source location to JSX:", error);
|
|
return {
|
|
code: code,
|
|
map: null,
|
|
};
|
|
}
|
|
},
|
|
};
|
|
}
|
|
function extractFilename(id) {
|
|
const pathParts = id.split("/");
|
|
const segmentIndex = findSegmentIndex(pathParts, ["pages", "components"]);
|
|
if (segmentIndex >= 0 && segmentIndex < pathParts.length - 1) {
|
|
const relevantParts = pathParts.slice(segmentIndex);
|
|
const last = relevantParts[relevantParts.length - 1];
|
|
relevantParts[relevantParts.length - 1] = stripExtension(last ?? "");
|
|
return relevantParts.join("/");
|
|
}
|
|
const lastPart = pathParts[pathParts.length - 1] ?? "";
|
|
return stripExtension(lastPart);
|
|
}
|
|
function findSegmentIndex(parts, segments) {
|
|
for (const segment of segments) {
|
|
const idx = parts.findIndex((part) => part === segment);
|
|
if (idx >= 0)
|
|
return idx;
|
|
}
|
|
return -1;
|
|
}
|
|
function stripExtension(filename) {
|
|
const dotIndex = filename.indexOf(".");
|
|
return dotIndex >= 0 ? filename.substring(0, dotIndex) : filename;
|
|
}
|
|
//# sourceMappingURL=visual-edit-plugin.js.map
|