@base44-preview/vite-plugin 0.1.0-dev.e65cc11 → 0.1.0-dev.ec354c8
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/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/error-overlay-plugin.d.ts +3 -0
- package/dist/error-overlay-plugin.d.ts.map +1 -0
- package/dist/error-overlay-plugin.js +14 -0
- package/dist/error-overlay-plugin.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/visual-edit-plugin.d.ts +11 -0
- package/dist/visual-edit-plugin.d.ts.map +1 -0
- package/dist/visual-edit-plugin.js +228 -0
- package/dist/visual-edit-plugin.js.map +1 -0
- package/package.json +9 -1
- package/src/ErrorOverlay.ts +71 -0
- package/src/error-overlay-plugin.ts +18 -0
- package/src/index.ts +7 -1
- package/src/visual-edit-plugin.ts +266 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare const HTMLElement: {
|
|
2
|
+
new (): {};
|
|
3
|
+
};
|
|
4
|
+
export declare class ErrorOverlay extends HTMLElement {
|
|
5
|
+
static getOverlayHTML(): string;
|
|
6
|
+
close(): void;
|
|
7
|
+
static sendErrorToParent(error: Error, title: string, details: string | undefined, componentName: string | undefined): void;
|
|
8
|
+
constructor(error: Error);
|
|
9
|
+
}
|
|
10
|
+
export declare const errorOverlayCode: string;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=ErrorOverlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorOverlay.d.ts","sourceRoot":"","sources":["../src/ErrorOverlay.ts"],"names":[],"mappings":"AAGA,QAAA,MAAQ,WAAW;;CAA0B,CAAC;AAE9C,qBAAa,YAAa,SAAQ,WAAW;IAC3C,MAAM,CAAC,cAAc;IAMrB,KAAK;IAIL,MAAM,CAAC,iBAAiB,CACtB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,aAAa,EAAE,MAAM,GAAG,SAAS;gBAqBvB,KAAK,EAAE,KAAK;CAuBzB;AAGD,eAAO,MAAM,gBAAgB,QAG5B,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
// Make HTMLElement available in non-browser environments
|
|
3
|
+
const { HTMLElement = class {
|
|
4
|
+
} } = globalThis;
|
|
5
|
+
export class ErrorOverlay extends HTMLElement {
|
|
6
|
+
static getOverlayHTML() {
|
|
7
|
+
return `
|
|
8
|
+
<div>
|
|
9
|
+
</div>
|
|
10
|
+
`;
|
|
11
|
+
}
|
|
12
|
+
close() {
|
|
13
|
+
this.parentNode?.removeChild(this);
|
|
14
|
+
}
|
|
15
|
+
static sendErrorToParent(error, title, details, componentName) {
|
|
16
|
+
// Send error to parent using framewire
|
|
17
|
+
if (globalThis.window?.parent) {
|
|
18
|
+
try {
|
|
19
|
+
globalThis.window.parent?.postMessage({
|
|
20
|
+
type: "app_error",
|
|
21
|
+
error: { title, details, componentName, originalError: error },
|
|
22
|
+
}, "*");
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.warn("Failed to send error to iframe parent:", error?.message);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
constructor(error) {
|
|
30
|
+
super();
|
|
31
|
+
const stack = error?.stack;
|
|
32
|
+
let componentName = stack?.match(/at\s+(\w+)\s+\(eval/)?.[1];
|
|
33
|
+
if (componentName === "eval") {
|
|
34
|
+
componentName = undefined;
|
|
35
|
+
}
|
|
36
|
+
const title = componentName
|
|
37
|
+
? `in ${componentName}: ${error.message?.toString()}`
|
|
38
|
+
: error.message?.toString();
|
|
39
|
+
const details = error?.stack;
|
|
40
|
+
// Call editor frame with the error (via post message)
|
|
41
|
+
ErrorOverlay.sendErrorToParent(error, title, details, componentName);
|
|
42
|
+
// Create the overlay element using HTML template
|
|
43
|
+
const overlay = document.createElement("div");
|
|
44
|
+
overlay.innerHTML = ErrorOverlay.getOverlayHTML();
|
|
45
|
+
// Add to DOM
|
|
46
|
+
document.body.appendChild(overlay);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// vite/react-plugin transpiles classes with _SomeClass, so we need to replace all _ErrorOverlay with ErrorOverlay
|
|
50
|
+
export const errorOverlayCode = ErrorOverlay.toString().replaceAll("_ErrorOverlay", "ErrorOverlay");
|
|
51
|
+
//# sourceMappingURL=ErrorOverlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorOverlay.js","sourceRoot":"","sources":["../src/ErrorOverlay.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAE3B,yDAAyD;AACzD,MAAM,EAAE,WAAW,GAAG;CAAQ,EAAE,GAAG,UAAU,CAAC;AAE9C,MAAM,OAAO,YAAa,SAAQ,WAAW;IAC3C,MAAM,CAAC,cAAc;QACnB,OAAO;;;KAGN,CAAC;IACJ,CAAC;IACD,KAAK;QACF,IAAY,CAAC,UAAU,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,CAAC,iBAAiB,CACtB,KAAY,EACZ,KAAa,EACb,OAA2B,EAC3B,aAAiC;QAEjC,uCAAuC;QACvC,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CACnC;oBACE,IAAI,EAAE,WAAW;oBACjB,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE;iBAC/D,EACD,GAAG,CACJ,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CACV,wCAAwC,EACvC,KAAe,EAAE,OAAO,CAC1B,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,YAAY,KAAY;QACtB,KAAK,EAAE,CAAC;QAER,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,CAAC;QAC3B,IAAI,aAAa,GAAG,KAAK,EAAE,KAAK,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7D,IAAI,aAAa,KAAK,MAAM,EAAE,CAAC;YAC7B,aAAa,GAAG,SAAS,CAAC;QAC5B,CAAC;QACD,MAAM,KAAK,GAAG,aAAa;YACzB,CAAC,CAAC,MAAM,aAAa,KAAK,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;YACrD,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,KAAK,EAAE,KAAK,CAAC;QAE7B,sDAAsD;QACtD,YAAY,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QAErE,iDAAiD;QACjD,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,CAAC,SAAS,GAAG,YAAY,CAAC,cAAc,EAAE,CAAC;QAElD,aAAa;QACb,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;CACF;AAED,kHAAkH;AAClH,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC,UAAU,CAChE,eAAe,EACf,cAAc,CACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-overlay-plugin.d.ts","sourceRoot":"","sources":["../src/error-overlay-plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,wBAAgB,kBAAkB,IAa3B,MAAM,CACZ"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { errorOverlayCode } from "./ErrorOverlay.js";
|
|
2
|
+
export function errorOverlayPlugin() {
|
|
3
|
+
return {
|
|
4
|
+
name: "error-overlay",
|
|
5
|
+
transform(code, id, opts = {}) {
|
|
6
|
+
if (opts?.ssr)
|
|
7
|
+
return;
|
|
8
|
+
if (!id.includes("vite/dist/client/client.mjs"))
|
|
9
|
+
return;
|
|
10
|
+
return code.replace("class ErrorOverlay", errorOverlayCode + "\nclass OldErrorOverlay");
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=error-overlay-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-overlay-plugin.js","sourceRoot":"","sources":["../src/error-overlay-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,MAAM,UAAU,kBAAkB;IAChC,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,GAAG,EAAE;YAC3B,IAAI,IAAI,EAAE,GAAG;gBAAE,OAAO;YAEtB,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;gBAAE,OAAO;YAExD,OAAO,IAAI,CAAC,OAAO,CACjB,oBAAoB,EACpB,gBAAgB,GAAG,yBAAyB,CAC7C,CAAC;QACJ,CAAC;KACQ,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,MAAM,CAAC;AAK/C,MAAM,CAAC,OAAO,UAAU,UAAU,CAChC,IAAI,GAAE;IACJ,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACvB,GAiID,MAAM,CACZ"}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { loadEnv } from "vite";
|
|
2
|
+
import { errorOverlayPlugin } from "./error-overlay-plugin.js";
|
|
3
|
+
import { visualEditPlugin } from "./visual-edit-plugin.js";
|
|
2
4
|
export default function vitePlugin(opts = {}) {
|
|
3
5
|
const { legacySDKImports = false } = opts;
|
|
4
6
|
return {
|
|
5
7
|
name: "base44",
|
|
8
|
+
enforce: "pre",
|
|
6
9
|
config: ({ mode }) => {
|
|
7
10
|
const env = loadEnv(mode ?? "development", process.cwd(), "");
|
|
8
|
-
const isRunningInSandbox =
|
|
11
|
+
const isRunningInSandbox = env.BASE44_SANDBOX_MODE === "true";
|
|
9
12
|
return {
|
|
10
13
|
resolve: {
|
|
11
14
|
alias: {
|
|
@@ -26,6 +29,9 @@ export default function vitePlugin(opts = {}) {
|
|
|
26
29
|
});
|
|
27
30
|
},
|
|
28
31
|
},
|
|
32
|
+
...(mode === "development"
|
|
33
|
+
? [errorOverlayPlugin(), visualEditPlugin()]
|
|
34
|
+
: []),
|
|
29
35
|
],
|
|
30
36
|
server: {
|
|
31
37
|
host: "0.0.0.0", // Bind to all interfaces for container access
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,CAAC,OAAO,UAAU,UAAU,CAChC,OAEI,EAAE;IAEN,MAAM,EAAE,gBAAgB,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC;IAE1C,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,KAAK;QACd,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;YACnB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,aAAa,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;YAC9D,MAAM,kBAAkB,GAAG,GAAG,CAAC,mBAAmB,KAAK,MAAM,CAAC;YAE9D,OAAO;gBACL,OAAO,EAAE;oBACP,KAAK,EAAE;wBACL,IAAI,EAAE,OAAO;qBACd;iBACF;gBACD,GAAG,CAAC,kBAAkB;oBACpB,CAAC,CAAE;wBACC,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,YAAY;gCAClB,eAAe,CAAC,MAAM;oCACpB,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;wCACxC,yBAAyB;wCACzB,GAAG,CAAC,SAAS,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;wCAC7C,GAAG,CAAC,SAAS,CACX,yBAAyB,EACzB,oBAAoB,CACrB,CAAC;wCACF,IAAI,EAAE,CAAC;oCACT,CAAC,CAAC,CAAC;gCACL,CAAC;6BACF;4BACD,GAAG,CAAC,IAAI,KAAK,aAAa;gCACxB,CAAC,CAAC,CAAC,kBAAkB,EAAE,EAAE,gBAAgB,EAAE,CAAC;gCAC5C,CAAC,CAAC,EAAE,CAAC;yBACR;wBACD,MAAM,EAAE;4BACN,IAAI,EAAE,SAAS,EAAE,8CAA8C;4BAC/D,IAAI,EAAE,IAAI;4BACV,UAAU,EAAE,IAAI;4BAChB,oDAAoD;4BACpD,YAAY,EAAE,IAAI;4BAClB,KAAK,EAAE;gCACL,gEAAgE;gCAChE,UAAU,EAAE,IAAI;gCAChB,QAAQ,EAAE,GAAG,EAAE,uCAAuC;6BACvD;4BACD,GAAG,EAAE;gCACH,QAAQ,EAAE,KAAK;gCACf,UAAU,EAAE,GAAG;6BAChB;yBACF;wBACD,KAAK,EAAE;4BACL,aAAa,EAAE;gCACb,MAAM,CAAC,OAAO,EAAE,IAAI;oCAClB,sCAAsC;oCACtC,IACE,OAAO,CAAC,IAAI,KAAK,mBAAmB;wCACpC,OAAO,CAAC,IAAI,KAAK,gBAAgB,EACjC,CAAC;wCACD,MAAM,IAAI,KAAK,CAAC,iBAAiB,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;oCACtD,CAAC;oCACD,iCAAiC;oCACjC,IAAI,CAAC,OAAO,CAAC,CAAC;gCAChB,CAAC;6BACF;yBACF;qBACsB;oBAC3B,CAAC,CAAC,EAAE,CAAC;gBACP,YAAY,EAAE;oBACZ,cAAc,EAAE;wBACd,MAAM,EAAE;4BACN,KAAK,EAAE,KAAK;yBACb;wBACD,GAAG,CAAC,gBAAgB;4BAClB,CAAC,CAAC;gCACE,MAAM,EAAE;oCACN,gCAAgC,EAAE,IAAI,CAAC,SAAS,CAC9C,GAAG,CAAC,kBAAkB,CACvB;oCACD,qCAAqC,EAAE,IAAI,CAAC,SAAS,CACnD,GAAG,CAAC,uBAAuB,CAC5B;iCACF;6BACF;4BACH,CAAC,CAAC,EAAE,CAAC;qBACR;iBACF;aACF,CAAC;QACJ,CAAC;QACD,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO;YACjC,IAAI,gBAAgB,EAAE,CAAC;gBACrB,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;oBACjC,OAAO,IAAI,CAAC,OAAO,CACjB,yCAAyC,EACzC,QAAQ,EACR,OAAO,CACR,CAAC;gBACJ,CAAC;gBAED,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;oBAClC,OAAO,IAAI,CAAC,OAAO,CACjB,0CAA0C,EAC1C,QAAQ,EACR,OAAO,CACR,CAAC;gBACJ,CAAC;gBAED,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;oBACrC,OAAO,IAAI,CAAC,OAAO,CACjB,6CAA6C,EAC7C,QAAQ,EACR,OAAO,CACR,CAAC;gBACJ,CAAC;gBAED,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;oBAChC,OAAO,IAAI,CAAC,OAAO,CACjB,uCAAuC,EACvC,QAAQ,EACR,OAAO,CACR,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;KACQ,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function visualEditPlugin(): {
|
|
2
|
+
name: string;
|
|
3
|
+
enforce: string;
|
|
4
|
+
order: string;
|
|
5
|
+
transformIndexHtml(html: any): any;
|
|
6
|
+
transform(code: any, id: any): {
|
|
7
|
+
code: any;
|
|
8
|
+
map: null;
|
|
9
|
+
} | null;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=visual-edit-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-edit-plugin.d.ts","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"AAkGA,wBAAgB,gBAAgB;;;;6BAMH,GAAG;oBAKZ,GAAG,MAAM,GAAG;;;;EA4J/B"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { parse } from "@babel/parser";
|
|
2
|
+
import { default as traverse } from "@babel/traverse";
|
|
3
|
+
import { default as generate } from "@babel/generator";
|
|
4
|
+
import * as t from "@babel/types";
|
|
5
|
+
// Helper function to check if JSX element contains dynamic content
|
|
6
|
+
function checkIfElementHasDynamicContent(jsxElement) {
|
|
7
|
+
let hasDynamicContent = false;
|
|
8
|
+
// Helper function to check if any node contains dynamic patterns
|
|
9
|
+
function checkNodeForDynamicContent(node) {
|
|
10
|
+
// JSX expressions like {variable}, {func()}, {obj.prop}
|
|
11
|
+
if (t.isJSXExpressionContainer(node)) {
|
|
12
|
+
const expression = node.expression;
|
|
13
|
+
// Skip empty expressions {}
|
|
14
|
+
if (t.isJSXEmptyExpression(expression)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
// Any non-literal expression is considered dynamic
|
|
18
|
+
if (!t.isLiteral(expression)) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Template literals with expressions `Hello ${name}`
|
|
23
|
+
if (t.isTemplateLiteral(node) && node.expressions.length > 0) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
// Member expressions like props.title, state.value
|
|
27
|
+
if (t.isMemberExpression(node)) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
// Function calls like getData(), format()
|
|
31
|
+
if (t.isCallExpression(node)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
// Conditional expressions like condition ? "yes" : "no"
|
|
35
|
+
if (t.isConditionalExpression(node)) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
// Identifier references (could be props, state, variables)
|
|
39
|
+
if (t.isIdentifier(node)) {
|
|
40
|
+
// Common dynamic identifiers
|
|
41
|
+
const dynamicNames = [
|
|
42
|
+
"props",
|
|
43
|
+
"state",
|
|
44
|
+
"data",
|
|
45
|
+
"item",
|
|
46
|
+
"value",
|
|
47
|
+
"text",
|
|
48
|
+
"content",
|
|
49
|
+
];
|
|
50
|
+
if (dynamicNames.some((name) => node.name.includes(name))) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
// Recursively traverse all child nodes
|
|
57
|
+
function traverseNode(node) {
|
|
58
|
+
if (checkNodeForDynamicContent(node)) {
|
|
59
|
+
hasDynamicContent = true;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Recursively check child nodes
|
|
63
|
+
Object.keys(node).forEach((key) => {
|
|
64
|
+
const value = node[key];
|
|
65
|
+
if (Array.isArray(value)) {
|
|
66
|
+
value.forEach((child) => {
|
|
67
|
+
if (child && typeof child === "object" && child.type) {
|
|
68
|
+
traverseNode(child);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else if (value && typeof value === "object" && value.type) {
|
|
73
|
+
traverseNode(value);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Check all children of the JSX element
|
|
78
|
+
jsxElement.children.forEach((child) => {
|
|
79
|
+
if (hasDynamicContent)
|
|
80
|
+
return; // Early exit if already found dynamic content
|
|
81
|
+
traverseNode(child);
|
|
82
|
+
});
|
|
83
|
+
return hasDynamicContent;
|
|
84
|
+
}
|
|
85
|
+
export function visualEditPlugin() {
|
|
86
|
+
return {
|
|
87
|
+
name: "visual-edit-transform",
|
|
88
|
+
enforce: "pre",
|
|
89
|
+
order: "pre",
|
|
90
|
+
// Inject Tailwind CDN for visual editing capabilities
|
|
91
|
+
transformIndexHtml(html) {
|
|
92
|
+
// Inject the Tailwind CSS CDN script right before the closing </head> tag
|
|
93
|
+
const tailwindScript = ` <!-- Tailwind CSS CDN for visual editing -->\n <script src="https://cdn.tailwindcss.com"></script>\n `;
|
|
94
|
+
return html.replace("</head>", tailwindScript + "</head>");
|
|
95
|
+
},
|
|
96
|
+
transform(code, id) {
|
|
97
|
+
// Skip node_modules and visual-edit-agent itself
|
|
98
|
+
if (id.includes("node_modules") || id.includes("visual-edit-agent")) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
// Process JS/JSX/TS/TSX files
|
|
102
|
+
if (!id.match(/\.(jsx?|tsx?)$/)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
// Extract filename from path, preserving pages/ or components/ structure
|
|
106
|
+
const pathParts = id.split("/");
|
|
107
|
+
let filename;
|
|
108
|
+
// Check if this is a pages or components file
|
|
109
|
+
if (id.includes("/pages/")) {
|
|
110
|
+
const pagesIndex = pathParts.findIndex((part) => part === "pages");
|
|
111
|
+
if (pagesIndex >= 0 && pagesIndex < pathParts.length - 1) {
|
|
112
|
+
// Get all parts from 'pages' to the file, preserving nested structure
|
|
113
|
+
const relevantParts = pathParts.slice(pagesIndex, pathParts.length);
|
|
114
|
+
const lastPart = relevantParts[relevantParts.length - 1];
|
|
115
|
+
// Remove file extension from the last part
|
|
116
|
+
relevantParts[relevantParts.length - 1] = lastPart.includes(".")
|
|
117
|
+
? lastPart.split(".")[0]
|
|
118
|
+
: lastPart;
|
|
119
|
+
filename = relevantParts.join("/");
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
filename = pathParts[pathParts.length - 1];
|
|
123
|
+
if (filename.includes(".")) {
|
|
124
|
+
filename = filename.split(".")[0];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (id.includes("/components/")) {
|
|
129
|
+
const componentsIndex = pathParts.findIndex((part) => part === "components");
|
|
130
|
+
if (componentsIndex >= 0 && componentsIndex < pathParts.length - 1) {
|
|
131
|
+
// Get all parts from 'components' to the file, preserving nested structure
|
|
132
|
+
const relevantParts = pathParts.slice(componentsIndex, pathParts.length);
|
|
133
|
+
const lastPart = relevantParts[relevantParts.length - 1];
|
|
134
|
+
// Remove file extension from the last part
|
|
135
|
+
relevantParts[relevantParts.length - 1] = lastPart.includes(".")
|
|
136
|
+
? lastPart.split(".")[0]
|
|
137
|
+
: lastPart;
|
|
138
|
+
filename = relevantParts.join("/");
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
filename = pathParts[pathParts.length - 1];
|
|
142
|
+
if (filename.includes(".")) {
|
|
143
|
+
filename = filename.split(".")[0];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// For other files (like layout), just use the filename
|
|
149
|
+
filename = pathParts[pathParts.length - 1];
|
|
150
|
+
if (filename.includes(".")) {
|
|
151
|
+
filename = filename.split(".")[0];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
// Parse the code into an AST
|
|
156
|
+
const ast = parse(code, {
|
|
157
|
+
sourceType: "module",
|
|
158
|
+
plugins: [
|
|
159
|
+
"jsx",
|
|
160
|
+
"typescript",
|
|
161
|
+
"decorators-legacy",
|
|
162
|
+
"classProperties",
|
|
163
|
+
"objectRestSpread",
|
|
164
|
+
"functionBind",
|
|
165
|
+
"exportDefaultFrom",
|
|
166
|
+
"exportNamespaceFrom",
|
|
167
|
+
"dynamicImport",
|
|
168
|
+
"nullishCoalescingOperator",
|
|
169
|
+
"optionalChaining",
|
|
170
|
+
"asyncGenerators",
|
|
171
|
+
"bigInt",
|
|
172
|
+
"optionalCatchBinding",
|
|
173
|
+
"throwExpressions",
|
|
174
|
+
],
|
|
175
|
+
});
|
|
176
|
+
// Traverse the AST and add source location and dynamic content attributes to JSX elements
|
|
177
|
+
let elementsProcessed = 0;
|
|
178
|
+
traverse.default(ast, {
|
|
179
|
+
JSXElement(path) {
|
|
180
|
+
const jsxElement = path.node;
|
|
181
|
+
const openingElement = jsxElement.openingElement;
|
|
182
|
+
// Skip fragments
|
|
183
|
+
if (t.isJSXFragment(jsxElement))
|
|
184
|
+
return;
|
|
185
|
+
// Skip if already has source location attribute
|
|
186
|
+
const hasSourceLocation = openingElement.attributes.some((attr) => t.isJSXAttribute(attr) &&
|
|
187
|
+
t.isJSXIdentifier(attr.name) &&
|
|
188
|
+
attr.name.name === "data-source-location");
|
|
189
|
+
if (hasSourceLocation)
|
|
190
|
+
return;
|
|
191
|
+
// Get line and column from AST node location
|
|
192
|
+
const { line, column } = openingElement.loc?.start || {
|
|
193
|
+
line: 1,
|
|
194
|
+
column: 0,
|
|
195
|
+
};
|
|
196
|
+
// Create the source location attribute
|
|
197
|
+
const sourceLocationAttr = t.jsxAttribute(t.jsxIdentifier("data-source-location"), t.stringLiteral(`${filename}:${line}:${column}`));
|
|
198
|
+
// Check if element has dynamic content
|
|
199
|
+
const isDynamic = checkIfElementHasDynamicContent(jsxElement);
|
|
200
|
+
// Create the dynamic content attribute
|
|
201
|
+
const dynamicContentAttr = t.jsxAttribute(t.jsxIdentifier("data-dynamic-content"), t.stringLiteral(isDynamic ? "true" : "false"));
|
|
202
|
+
// Add both attributes to the beginning of the attributes array
|
|
203
|
+
openingElement.attributes.unshift(sourceLocationAttr, dynamicContentAttr);
|
|
204
|
+
elementsProcessed++;
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
// Generate the code back from the AST
|
|
208
|
+
const result = generate.default(ast, {
|
|
209
|
+
compact: false,
|
|
210
|
+
concise: false,
|
|
211
|
+
retainLines: true,
|
|
212
|
+
});
|
|
213
|
+
return {
|
|
214
|
+
code: result.code,
|
|
215
|
+
map: null,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.error("Failed to add source location to JSX:", error);
|
|
220
|
+
return {
|
|
221
|
+
code: code, // Return original code on failure
|
|
222
|
+
map: null,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=visual-edit-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-edit-plugin.js","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@base44-preview/vite-plugin",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.ec354c8",
|
|
4
4
|
"description": "The Vite plugin for base44 based applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,8 +17,16 @@
|
|
|
17
17
|
"build": "tsc"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
+
"@types/babel__generator": "^7.27.0",
|
|
21
|
+
"@types/babel__traverse": "^7.28.0",
|
|
20
22
|
"@types/node": "^24.6.2",
|
|
21
23
|
"typescript": "^5.9.3",
|
|
22
24
|
"vite": "^7.1.9"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@babel/generator": "^7.28.5",
|
|
28
|
+
"@babel/parser": "^7.28.5",
|
|
29
|
+
"@babel/traverse": "^7.28.5",
|
|
30
|
+
"@babel/types": "^7.28.5"
|
|
23
31
|
}
|
|
24
32
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
|
|
3
|
+
// Make HTMLElement available in non-browser environments
|
|
4
|
+
const { HTMLElement = class {} } = globalThis;
|
|
5
|
+
|
|
6
|
+
export class ErrorOverlay extends HTMLElement {
|
|
7
|
+
static getOverlayHTML() {
|
|
8
|
+
return `
|
|
9
|
+
<div>
|
|
10
|
+
</div>
|
|
11
|
+
`;
|
|
12
|
+
}
|
|
13
|
+
close() {
|
|
14
|
+
(this as any).parentNode?.removeChild(this);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static sendErrorToParent(
|
|
18
|
+
error: Error,
|
|
19
|
+
title: string,
|
|
20
|
+
details: string | undefined,
|
|
21
|
+
componentName: string | undefined
|
|
22
|
+
) {
|
|
23
|
+
// Send error to parent using framewire
|
|
24
|
+
if (globalThis.window?.parent) {
|
|
25
|
+
try {
|
|
26
|
+
globalThis.window.parent?.postMessage(
|
|
27
|
+
{
|
|
28
|
+
type: "app_error",
|
|
29
|
+
error: { title, details, componentName, originalError: error },
|
|
30
|
+
},
|
|
31
|
+
"*"
|
|
32
|
+
);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.warn(
|
|
35
|
+
"Failed to send error to iframe parent:",
|
|
36
|
+
(error as Error)?.message
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
constructor(error: Error) {
|
|
43
|
+
super();
|
|
44
|
+
|
|
45
|
+
const stack = error?.stack;
|
|
46
|
+
let componentName = stack?.match(/at\s+(\w+)\s+\(eval/)?.[1];
|
|
47
|
+
if (componentName === "eval") {
|
|
48
|
+
componentName = undefined;
|
|
49
|
+
}
|
|
50
|
+
const title = componentName
|
|
51
|
+
? `in ${componentName}: ${error.message?.toString()}`
|
|
52
|
+
: error.message?.toString();
|
|
53
|
+
const details = error?.stack;
|
|
54
|
+
|
|
55
|
+
// Call editor frame with the error (via post message)
|
|
56
|
+
ErrorOverlay.sendErrorToParent(error, title, details, componentName);
|
|
57
|
+
|
|
58
|
+
// Create the overlay element using HTML template
|
|
59
|
+
const overlay = document.createElement("div");
|
|
60
|
+
overlay.innerHTML = ErrorOverlay.getOverlayHTML();
|
|
61
|
+
|
|
62
|
+
// Add to DOM
|
|
63
|
+
document.body.appendChild(overlay);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// vite/react-plugin transpiles classes with _SomeClass, so we need to replace all _ErrorOverlay with ErrorOverlay
|
|
68
|
+
export const errorOverlayCode = ErrorOverlay.toString().replaceAll(
|
|
69
|
+
"_ErrorOverlay",
|
|
70
|
+
"ErrorOverlay"
|
|
71
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { errorOverlayCode } from "./ErrorOverlay.js";
|
|
2
|
+
import type { Plugin } from "vite";
|
|
3
|
+
|
|
4
|
+
export function errorOverlayPlugin() {
|
|
5
|
+
return {
|
|
6
|
+
name: "error-overlay",
|
|
7
|
+
transform(code, id, opts = {}) {
|
|
8
|
+
if (opts?.ssr) return;
|
|
9
|
+
|
|
10
|
+
if (!id.includes("vite/dist/client/client.mjs")) return;
|
|
11
|
+
|
|
12
|
+
return code.replace(
|
|
13
|
+
"class ErrorOverlay",
|
|
14
|
+
errorOverlayCode + "\nclass OldErrorOverlay"
|
|
15
|
+
);
|
|
16
|
+
},
|
|
17
|
+
} as Plugin;
|
|
18
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Plugin, UserConfig } from "vite";
|
|
2
2
|
import { loadEnv } from "vite";
|
|
3
|
+
import { errorOverlayPlugin } from "./error-overlay-plugin.js";
|
|
4
|
+
import { visualEditPlugin } from "./visual-edit-plugin.js";
|
|
3
5
|
|
|
4
6
|
export default function vitePlugin(
|
|
5
7
|
opts: {
|
|
@@ -10,9 +12,10 @@ export default function vitePlugin(
|
|
|
10
12
|
|
|
11
13
|
return {
|
|
12
14
|
name: "base44",
|
|
15
|
+
enforce: "pre",
|
|
13
16
|
config: ({ mode }) => {
|
|
14
17
|
const env = loadEnv(mode ?? "development", process.cwd(), "");
|
|
15
|
-
const isRunningInSandbox =
|
|
18
|
+
const isRunningInSandbox = env.BASE44_SANDBOX_MODE === "true";
|
|
16
19
|
|
|
17
20
|
return {
|
|
18
21
|
resolve: {
|
|
@@ -37,6 +40,9 @@ export default function vitePlugin(
|
|
|
37
40
|
});
|
|
38
41
|
},
|
|
39
42
|
},
|
|
43
|
+
...(mode === "development"
|
|
44
|
+
? [errorOverlayPlugin(), visualEditPlugin()]
|
|
45
|
+
: []),
|
|
40
46
|
],
|
|
41
47
|
server: {
|
|
42
48
|
host: "0.0.0.0", // Bind to all interfaces for container access
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { parse } from "@babel/parser";
|
|
2
|
+
import { default as traverse } from "@babel/traverse";
|
|
3
|
+
import { default as generate } from "@babel/generator";
|
|
4
|
+
import * as t from "@babel/types";
|
|
5
|
+
|
|
6
|
+
// Helper function to check if JSX element contains dynamic content
|
|
7
|
+
function checkIfElementHasDynamicContent(jsxElement: any) {
|
|
8
|
+
let hasDynamicContent = false;
|
|
9
|
+
|
|
10
|
+
// Helper function to check if any node contains dynamic patterns
|
|
11
|
+
function checkNodeForDynamicContent(node: any) {
|
|
12
|
+
// JSX expressions like {variable}, {func()}, {obj.prop}
|
|
13
|
+
if (t.isJSXExpressionContainer(node)) {
|
|
14
|
+
const expression = node.expression;
|
|
15
|
+
|
|
16
|
+
// Skip empty expressions {}
|
|
17
|
+
if (t.isJSXEmptyExpression(expression)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Any non-literal expression is considered dynamic
|
|
22
|
+
if (!t.isLiteral(expression)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Template literals with expressions `Hello ${name}`
|
|
28
|
+
if (t.isTemplateLiteral(node) && node.expressions.length > 0) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Member expressions like props.title, state.value
|
|
33
|
+
if (t.isMemberExpression(node)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Function calls like getData(), format()
|
|
38
|
+
if (t.isCallExpression(node)) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Conditional expressions like condition ? "yes" : "no"
|
|
43
|
+
if (t.isConditionalExpression(node)) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Identifier references (could be props, state, variables)
|
|
48
|
+
if (t.isIdentifier(node)) {
|
|
49
|
+
// Common dynamic identifiers
|
|
50
|
+
const dynamicNames = [
|
|
51
|
+
"props",
|
|
52
|
+
"state",
|
|
53
|
+
"data",
|
|
54
|
+
"item",
|
|
55
|
+
"value",
|
|
56
|
+
"text",
|
|
57
|
+
"content",
|
|
58
|
+
];
|
|
59
|
+
if (dynamicNames.some((name) => node.name.includes(name))) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Recursively traverse all child nodes
|
|
68
|
+
function traverseNode(node: any) {
|
|
69
|
+
if (checkNodeForDynamicContent(node)) {
|
|
70
|
+
hasDynamicContent = true;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Recursively check child nodes
|
|
75
|
+
Object.keys(node).forEach((key) => {
|
|
76
|
+
const value = node[key];
|
|
77
|
+
|
|
78
|
+
if (Array.isArray(value)) {
|
|
79
|
+
value.forEach((child) => {
|
|
80
|
+
if (child && typeof child === "object" && child.type) {
|
|
81
|
+
traverseNode(child);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
} else if (value && typeof value === "object" && value.type) {
|
|
85
|
+
traverseNode(value);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check all children of the JSX element
|
|
91
|
+
jsxElement.children.forEach((child: any) => {
|
|
92
|
+
if (hasDynamicContent) return; // Early exit if already found dynamic content
|
|
93
|
+
traverseNode(child);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return hasDynamicContent;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function visualEditPlugin() {
|
|
100
|
+
return {
|
|
101
|
+
name: "visual-edit-transform",
|
|
102
|
+
enforce: "pre",
|
|
103
|
+
order: "pre",
|
|
104
|
+
// Inject Tailwind CDN for visual editing capabilities
|
|
105
|
+
transformIndexHtml(html: any) {
|
|
106
|
+
// Inject the Tailwind CSS CDN script right before the closing </head> tag
|
|
107
|
+
const tailwindScript = ` <!-- Tailwind CSS CDN for visual editing -->\n <script src="https://cdn.tailwindcss.com"></script>\n `;
|
|
108
|
+
return html.replace("</head>", tailwindScript + "</head>");
|
|
109
|
+
},
|
|
110
|
+
transform(code: any, id: any) {
|
|
111
|
+
// Skip node_modules and visual-edit-agent itself
|
|
112
|
+
if (id.includes("node_modules") || id.includes("visual-edit-agent")) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Process JS/JSX/TS/TSX files
|
|
117
|
+
if (!id.match(/\.(jsx?|tsx?)$/)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Extract filename from path, preserving pages/ or components/ structure
|
|
122
|
+
const pathParts = id.split("/");
|
|
123
|
+
let filename;
|
|
124
|
+
|
|
125
|
+
// Check if this is a pages or components file
|
|
126
|
+
if (id.includes("/pages/")) {
|
|
127
|
+
const pagesIndex = pathParts.findIndex((part: any) => part === "pages");
|
|
128
|
+
if (pagesIndex >= 0 && pagesIndex < pathParts.length - 1) {
|
|
129
|
+
// Get all parts from 'pages' to the file, preserving nested structure
|
|
130
|
+
const relevantParts = pathParts.slice(pagesIndex, pathParts.length);
|
|
131
|
+
const lastPart = relevantParts[relevantParts.length - 1];
|
|
132
|
+
// Remove file extension from the last part
|
|
133
|
+
relevantParts[relevantParts.length - 1] = lastPart.includes(".")
|
|
134
|
+
? lastPart.split(".")[0]
|
|
135
|
+
: lastPart;
|
|
136
|
+
filename = relevantParts.join("/");
|
|
137
|
+
} else {
|
|
138
|
+
filename = pathParts[pathParts.length - 1];
|
|
139
|
+
if (filename.includes(".")) {
|
|
140
|
+
filename = filename.split(".")[0];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} else if (id.includes("/components/")) {
|
|
144
|
+
const componentsIndex = pathParts.findIndex(
|
|
145
|
+
(part: any) => part === "components"
|
|
146
|
+
);
|
|
147
|
+
if (componentsIndex >= 0 && componentsIndex < pathParts.length - 1) {
|
|
148
|
+
// Get all parts from 'components' to the file, preserving nested structure
|
|
149
|
+
const relevantParts = pathParts.slice(
|
|
150
|
+
componentsIndex,
|
|
151
|
+
pathParts.length
|
|
152
|
+
);
|
|
153
|
+
const lastPart = relevantParts[relevantParts.length - 1];
|
|
154
|
+
// Remove file extension from the last part
|
|
155
|
+
relevantParts[relevantParts.length - 1] = lastPart.includes(".")
|
|
156
|
+
? lastPart.split(".")[0]
|
|
157
|
+
: lastPart;
|
|
158
|
+
filename = relevantParts.join("/");
|
|
159
|
+
} else {
|
|
160
|
+
filename = pathParts[pathParts.length - 1];
|
|
161
|
+
if (filename.includes(".")) {
|
|
162
|
+
filename = filename.split(".")[0];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
// For other files (like layout), just use the filename
|
|
167
|
+
filename = pathParts[pathParts.length - 1];
|
|
168
|
+
if (filename.includes(".")) {
|
|
169
|
+
filename = filename.split(".")[0];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
// Parse the code into an AST
|
|
175
|
+
const ast = parse(code, {
|
|
176
|
+
sourceType: "module",
|
|
177
|
+
plugins: [
|
|
178
|
+
"jsx",
|
|
179
|
+
"typescript",
|
|
180
|
+
"decorators-legacy",
|
|
181
|
+
"classProperties",
|
|
182
|
+
"objectRestSpread",
|
|
183
|
+
"functionBind",
|
|
184
|
+
"exportDefaultFrom",
|
|
185
|
+
"exportNamespaceFrom",
|
|
186
|
+
"dynamicImport",
|
|
187
|
+
"nullishCoalescingOperator",
|
|
188
|
+
"optionalChaining",
|
|
189
|
+
"asyncGenerators",
|
|
190
|
+
"bigInt",
|
|
191
|
+
"optionalCatchBinding",
|
|
192
|
+
"throwExpressions",
|
|
193
|
+
],
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Traverse the AST and add source location and dynamic content attributes to JSX elements
|
|
197
|
+
let elementsProcessed = 0;
|
|
198
|
+
traverse.default(ast, {
|
|
199
|
+
JSXElement(path) {
|
|
200
|
+
const jsxElement = path.node;
|
|
201
|
+
const openingElement = jsxElement.openingElement;
|
|
202
|
+
|
|
203
|
+
// Skip fragments
|
|
204
|
+
if (t.isJSXFragment(jsxElement)) return;
|
|
205
|
+
|
|
206
|
+
// Skip if already has source location attribute
|
|
207
|
+
const hasSourceLocation = openingElement.attributes.some(
|
|
208
|
+
(attr) =>
|
|
209
|
+
t.isJSXAttribute(attr) &&
|
|
210
|
+
t.isJSXIdentifier(attr.name) &&
|
|
211
|
+
attr.name.name === "data-source-location"
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
if (hasSourceLocation) return;
|
|
215
|
+
|
|
216
|
+
// Get line and column from AST node location
|
|
217
|
+
const { line, column } = openingElement.loc?.start || {
|
|
218
|
+
line: 1,
|
|
219
|
+
column: 0,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Create the source location attribute
|
|
223
|
+
const sourceLocationAttr = t.jsxAttribute(
|
|
224
|
+
t.jsxIdentifier("data-source-location"),
|
|
225
|
+
t.stringLiteral(`${filename}:${line}:${column}`)
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// Check if element has dynamic content
|
|
229
|
+
const isDynamic = checkIfElementHasDynamicContent(jsxElement);
|
|
230
|
+
|
|
231
|
+
// Create the dynamic content attribute
|
|
232
|
+
const dynamicContentAttr = t.jsxAttribute(
|
|
233
|
+
t.jsxIdentifier("data-dynamic-content"),
|
|
234
|
+
t.stringLiteral(isDynamic ? "true" : "false")
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
// Add both attributes to the beginning of the attributes array
|
|
238
|
+
openingElement.attributes.unshift(
|
|
239
|
+
sourceLocationAttr,
|
|
240
|
+
dynamicContentAttr
|
|
241
|
+
);
|
|
242
|
+
elementsProcessed++;
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Generate the code back from the AST
|
|
247
|
+
const result = generate.default(ast, {
|
|
248
|
+
compact: false,
|
|
249
|
+
concise: false,
|
|
250
|
+
retainLines: true,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
code: result.code,
|
|
255
|
+
map: null,
|
|
256
|
+
};
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error("Failed to add source location to JSX:", error);
|
|
259
|
+
return {
|
|
260
|
+
code: code, // Return original code on failure
|
|
261
|
+
map: null,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|