@base44/vite-plugin 1.0.26 → 1.0.30
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 +56 -0
- package/dist/build-status-plugin.d.ts +8 -0
- package/dist/build-status-plugin.d.ts.map +1 -0
- package/dist/build-status-plugin.js +170 -0
- package/dist/build-status-plugin.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/visual-edit-plugin.js +13 -21
- package/dist/visual-edit-plugin.js.map +1 -1
- package/package.json +1 -1
- package/src/build-status-plugin.ts +193 -0
- package/src/index.ts +8 -0
- package/src/visual-edit-plugin.md +10 -11
- package/src/visual-edit-plugin.ts +13 -24
package/README.md
CHANGED
|
@@ -48,6 +48,62 @@ The plugin registers four sub-plugins when running in a sandbox (`MODAL_SANDBOX_
|
|
|
48
48
|
|
|
49
49
|
Outside a sandbox, only the core plugin runs (with optional API proxy support via `VITE_BASE44_APP_BASE_URL`).
|
|
50
50
|
|
|
51
|
+
### Build-status endpoint
|
|
52
|
+
|
|
53
|
+
An opt-in, dev-only (`apply: "serve"`) endpoint that reports the last build /
|
|
54
|
+
compile / HMR error observed by the dev server. It exists solely to let the
|
|
55
|
+
preview backend poll for post-load errors — it is **never** part of a
|
|
56
|
+
production build.
|
|
57
|
+
|
|
58
|
+
Enable it by setting the environment variable at dev-server start:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
BASE44_BUILD_STATUS_ENABLED=1
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The env var is checked when the plugin assembles its plugin list (config load),
|
|
65
|
+
so the generated app's `vite.config.js` does not need to change. When it is
|
|
66
|
+
unset, the build-status sub-plugin is not added to the pipeline at all — its
|
|
67
|
+
`configureServer` hook is never reached.
|
|
68
|
+
|
|
69
|
+
When enabled, the plugin serves:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
GET /__build_status
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- **Loopback-only.** Requests from any non-loopback `remoteAddress`
|
|
76
|
+
(`127.0.0.1`, `::1`, `::ffff:127.0.0.1`) fall through as if the route did
|
|
77
|
+
not exist. Non-`GET` methods fall through too.
|
|
78
|
+
- **Always `200`.** Health lives in the body, not the status code — a `500`
|
|
79
|
+
would trip the preview proxy's own error path. The body is:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{ "ok": true, "error": null }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
or, when an error is outstanding:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{ "ok": false, "error": { "message": "…", "frame": "…", "loc": { "…": 0 } } }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- **Scrubbed.** Even though the endpoint is loopback-only (the backend relays a
|
|
92
|
+
trimmed form onward), the error is size-capped and absolute filesystem paths
|
|
93
|
+
are stripped down to basenames.
|
|
94
|
+
- **Fail-open.** All setup and request handling is wrapped in `try/catch` so a
|
|
95
|
+
bug in this plugin can never break the dev server.
|
|
96
|
+
|
|
97
|
+
How it works: `configureServer` wraps the HMR send channel
|
|
98
|
+
(`server.hot ?? server.ws`). An `error` payload records the last error; a
|
|
99
|
+
successful `update` or `full-reload` clears it.
|
|
100
|
+
|
|
101
|
+
**Cold-start caveat & scope.** Errors thrown *before any client has connected*
|
|
102
|
+
are not sent over the HMR channel, so a boot-time transform failure may briefly
|
|
103
|
+
read `ok: true`. That window is already covered server-side by the existing
|
|
104
|
+
`dev_server_error_classifier` (install / start failures surface as `409`). This
|
|
105
|
+
endpoint's job is **post-load HMR / compile errors**, not cold-start failures.
|
|
106
|
+
|
|
51
107
|
### Sandbox Injections
|
|
52
108
|
|
|
53
109
|
In **dev mode**, individual `<script type="module" src="...">` tags are injected for each feature, loaded directly from `node_modules`:
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
/**
|
|
3
|
+
* Produce a trimmed, path-stripped, size-capped view of an HMR error
|
|
4
|
+
* payload. Returns `null` when there is no error.
|
|
5
|
+
*/
|
|
6
|
+
export declare function scrub(err: unknown): Record<string, unknown> | null;
|
|
7
|
+
export declare function buildStatusPlugin(): Plugin;
|
|
8
|
+
//# sourceMappingURL=build-status-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-status-plugin.d.ts","sourceRoot":"","sources":["../src/build-status-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,MAAM,EAAiB,MAAM,MAAM,CAAC;AAyE3D;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CA2ClE;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAsE1C"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in, loopback-gated dev-server endpoint that reports the last
|
|
3
|
+
* build / compile / HMR error observed since the dev server started.
|
|
4
|
+
*
|
|
5
|
+
* `GET /__build_status` responds `200` with a JSON body:
|
|
6
|
+
* { ok: boolean, error: <scrubbed error> | null }
|
|
7
|
+
*
|
|
8
|
+
* The endpoint is:
|
|
9
|
+
* - dev-only (`apply: "serve"`) — it never exists in a production build;
|
|
10
|
+
* - loopback-only — requests from non-loopback addresses fall through.
|
|
11
|
+
*
|
|
12
|
+
* Enablement (the `BASE44_BUILD_STATUS_ENABLED` env-var check) is decided by
|
|
13
|
+
* the caller in `index.ts`, which only adds this plugin to the array when the
|
|
14
|
+
* endpoint is wanted — so `configureServer` here is never even reached when
|
|
15
|
+
* the feature is off.
|
|
16
|
+
*
|
|
17
|
+
* The status is always reported in the body with HTTP `200`, even on error:
|
|
18
|
+
* a `500` here would trip the preview proxy's own error path.
|
|
19
|
+
*
|
|
20
|
+
* Cold-start caveat: errors thrown before any client has connected are not
|
|
21
|
+
* sent over the HMR channel, so a boot-time transform failure may briefly
|
|
22
|
+
* read `ok: true`. That window is covered server-side by the existing
|
|
23
|
+
* dev_server_error_classifier. This endpoint reports post-load
|
|
24
|
+
* HMR / compile errors.
|
|
25
|
+
*/
|
|
26
|
+
const ENDPOINT = "/__build_status";
|
|
27
|
+
/** Max characters kept per scrubbed string field. */
|
|
28
|
+
const MAX_FIELD = 4000;
|
|
29
|
+
/** Max characters kept for the code frame. */
|
|
30
|
+
const MAX_FRAME = 2000;
|
|
31
|
+
const LOOPBACK_ADDRESSES = new Set([
|
|
32
|
+
"127.0.0.1",
|
|
33
|
+
"::1",
|
|
34
|
+
"::ffff:127.0.0.1",
|
|
35
|
+
]);
|
|
36
|
+
function isLoopback(remoteAddress) {
|
|
37
|
+
return remoteAddress !== undefined && LOOPBACK_ADDRESSES.has(remoteAddress);
|
|
38
|
+
}
|
|
39
|
+
function clip(value, max) {
|
|
40
|
+
if (typeof value !== "string" || value.length === 0)
|
|
41
|
+
return undefined;
|
|
42
|
+
return value.length > max ? `${value.slice(0, max)}… [truncated]` : value;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Strip absolute filesystem paths down to their basename so directory
|
|
46
|
+
* structure never leaks. This is loopback-only, but the backend relays a
|
|
47
|
+
* trimmed form, so scrub before it leaves this process.
|
|
48
|
+
*/
|
|
49
|
+
function stripPaths(value) {
|
|
50
|
+
return (value
|
|
51
|
+
// Windows: C:\Users\foo\bar\file.ext -> file.ext
|
|
52
|
+
.replace(/[A-Za-z]:\\[^\s:*?"<>|]+/g, (m) => m.split(/[\\/]/).pop() ?? m)
|
|
53
|
+
// POSIX absolute: /Users/foo/bar/file.ext -> file.ext
|
|
54
|
+
.replace(/\/(?:[^\s:/]+\/)+[^\s:/]+/g, (m) => m.split("/").pop() ?? m));
|
|
55
|
+
}
|
|
56
|
+
function scrubString(value, max) {
|
|
57
|
+
const clipped = clip(value, max);
|
|
58
|
+
return clipped === undefined ? undefined : stripPaths(clipped);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Produce a trimmed, path-stripped, size-capped view of an HMR error
|
|
62
|
+
* payload. Returns `null` when there is no error.
|
|
63
|
+
*/
|
|
64
|
+
export function scrub(err) {
|
|
65
|
+
if (err === null || err === undefined)
|
|
66
|
+
return null;
|
|
67
|
+
if (typeof err !== "object") {
|
|
68
|
+
const message = scrubString(String(err), MAX_FIELD);
|
|
69
|
+
return message === undefined ? null : { message };
|
|
70
|
+
}
|
|
71
|
+
const e = err;
|
|
72
|
+
const out = {};
|
|
73
|
+
const message = scrubString(e["message"], MAX_FIELD);
|
|
74
|
+
if (message !== undefined)
|
|
75
|
+
out["message"] = message;
|
|
76
|
+
const stack = scrubString(e["stack"], MAX_FIELD);
|
|
77
|
+
if (stack !== undefined)
|
|
78
|
+
out["stack"] = stack;
|
|
79
|
+
const frame = scrubString(e["frame"], MAX_FRAME);
|
|
80
|
+
if (frame !== undefined)
|
|
81
|
+
out["frame"] = frame;
|
|
82
|
+
const id = scrubString(e["id"], MAX_FIELD);
|
|
83
|
+
if (id !== undefined)
|
|
84
|
+
out["id"] = id;
|
|
85
|
+
const plugin = scrubString(e["plugin"], 200);
|
|
86
|
+
if (plugin !== undefined)
|
|
87
|
+
out["plugin"] = plugin;
|
|
88
|
+
const loc = e["loc"];
|
|
89
|
+
if (loc && typeof loc === "object") {
|
|
90
|
+
const l = loc;
|
|
91
|
+
out["loc"] = {
|
|
92
|
+
file: scrubString(l["file"], MAX_FIELD),
|
|
93
|
+
line: typeof l["line"] === "number" ? l["line"] : undefined,
|
|
94
|
+
column: typeof l["column"] === "number" ? l["column"] : undefined,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Fall back to a stringified form if we recognised nothing useful.
|
|
98
|
+
if (Object.keys(out).length === 0) {
|
|
99
|
+
const message = scrubString(String(e), MAX_FIELD);
|
|
100
|
+
return message === undefined ? {} : { message };
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
export function buildStatusPlugin() {
|
|
105
|
+
let lastError = null;
|
|
106
|
+
return {
|
|
107
|
+
name: "base44-build-status",
|
|
108
|
+
apply: "serve",
|
|
109
|
+
configureServer(server) {
|
|
110
|
+
try {
|
|
111
|
+
// Observe the HMR channel: an `error` payload records the last
|
|
112
|
+
// error; a successful `update` / `full-reload` clears it.
|
|
113
|
+
const ch = (server.hot ?? server.ws);
|
|
114
|
+
if (ch && typeof ch.send === "function") {
|
|
115
|
+
const orig = ch.send.bind(ch);
|
|
116
|
+
ch.send = (payload, ...rest) => {
|
|
117
|
+
try {
|
|
118
|
+
const p = payload;
|
|
119
|
+
if (p?.type === "error") {
|
|
120
|
+
lastError = p.err ?? null;
|
|
121
|
+
}
|
|
122
|
+
else if (p?.type === "update" || p?.type === "full-reload") {
|
|
123
|
+
lastError = null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Never let bookkeeping break HMR delivery.
|
|
128
|
+
}
|
|
129
|
+
return orig(payload, ...rest);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
server.middlewares.use(ENDPOINT, (req, res, next) => {
|
|
133
|
+
try {
|
|
134
|
+
if (!isLoopback(req.socket?.remoteAddress)) {
|
|
135
|
+
next();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (req.method !== "GET") {
|
|
139
|
+
next();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const body = JSON.stringify({
|
|
143
|
+
ok: !lastError,
|
|
144
|
+
error: scrub(lastError),
|
|
145
|
+
});
|
|
146
|
+
// Health lives in the body — keep 200 even on error so the
|
|
147
|
+
// preview proxy doesn't treat this as a server failure.
|
|
148
|
+
res.statusCode = 200;
|
|
149
|
+
res.setHeader("Content-Type", "application/json");
|
|
150
|
+
res.setHeader("Cache-Control", "no-store");
|
|
151
|
+
res.end(body);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Fail open: a bug here must never break the dev server.
|
|
155
|
+
try {
|
|
156
|
+
next();
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// ignore
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Fail open: setup errors must never break the dev server.
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=build-status-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-status-plugin.js","sourceRoot":"","sources":["../src/build-status-plugin.ts"],"names":[],"mappings":"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"}
|
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;AAU/C,MAAM,CAAC,OAAO,UAAU,UAAU,CAChC,IAAI,GAAE;IACJ,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACvB,iBA+NP"}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { errorOverlayPlugin } from "./error-overlay-plugin.js";
|
|
|
3
3
|
import { visualEditPlugin } from "./visual-edit-plugin.js";
|
|
4
4
|
import { filterPackagesInProject } from "./utils.js";
|
|
5
5
|
import { htmlInjectionsPlugin } from "./html-injections-plugin.js";
|
|
6
|
+
import { buildStatusPlugin } from "./build-status-plugin.js";
|
|
6
7
|
const isRunningInSandbox = !!process.env.MODAL_SANDBOX_ID;
|
|
7
8
|
export default function vitePlugin(opts = {}) {
|
|
8
9
|
const { legacySDKImports = false, hmrNotifier = false, navigationNotifier = false, visualEditAgent = false, analyticsTracker = false, } = opts;
|
|
@@ -161,6 +162,13 @@ export default function vitePlugin(opts = {}) {
|
|
|
161
162
|
: []),
|
|
162
163
|
// HTML injections - handles both dev (sandbox) and production injections
|
|
163
164
|
htmlInjectionsPlugin({ hmrNotifier, navigationNotifier, visualEditAgent, analyticsTracker }),
|
|
165
|
+
// Opt-in, loopback-gated dev-server build-status endpoint. Gated here on
|
|
166
|
+
// the BASE44_BUILD_STATUS_ENABLED env var so the generated app's
|
|
167
|
+
// vite.config.js doesn't need to change and the sub-plugin isn't even
|
|
168
|
+
// added to the pipeline when disabled. The endpoint itself is dev-only.
|
|
169
|
+
...(process.env.BASE44_BUILD_STATUS_ENABLED === "1"
|
|
170
|
+
? [buildStatusPlugin()]
|
|
171
|
+
: []),
|
|
164
172
|
];
|
|
165
173
|
}
|
|
166
174
|
//# sourceMappingURL=index.js.map
|
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;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -73,28 +73,20 @@ export function visualEditPlugin() {
|
|
|
73
73
|
},
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
|
+
// Emit the canonical real path (src/pages/Home.jsx) the backend and sandbox use, NOT a
|
|
77
|
+
// legacy alias (pages/Home). The path is anchored on the last "src" segment with the
|
|
78
|
+
// extension retained, so consumers get the file's true sandbox path directly.
|
|
76
79
|
function extractFilename(id) {
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
// Drop any Vite query suffix (e.g. "?v=...") and normalize separators.
|
|
81
|
+
const cleanId = id.split("?")[0].replace(/\\/g, "/");
|
|
82
|
+
const parts = cleanId.split("/");
|
|
83
|
+
// Anchor on the LAST "src" segment so a "src" elsewhere in the absolute path
|
|
84
|
+
// (e.g. a user's home dir) can't mis-anchor. base44 apps keep all source under src/.
|
|
85
|
+
const srcIndex = parts.lastIndexOf("src");
|
|
86
|
+
if (srcIndex >= 0) {
|
|
87
|
+
return parts.slice(srcIndex).join("/"); // src/pages/Home.jsx, src/Layout.jsx, src/components/ui/Card.tsx
|
|
84
88
|
}
|
|
85
|
-
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
function findSegmentIndex(parts, segments) {
|
|
89
|
-
for (const segment of segments) {
|
|
90
|
-
const idx = parts.findIndex((part) => part === segment);
|
|
91
|
-
if (idx >= 0)
|
|
92
|
-
return idx;
|
|
93
|
-
}
|
|
94
|
-
return -1;
|
|
95
|
-
}
|
|
96
|
-
function stripExtension(filename) {
|
|
97
|
-
const dotIndex = filename.indexOf(".");
|
|
98
|
-
return dotIndex >= 0 ? filename.substring(0, dotIndex) : filename;
|
|
89
|
+
// Fallback: file not under src/ — use the bare filename (extension kept).
|
|
90
|
+
return parts[parts.length - 1] ?? "";
|
|
99
91
|
}
|
|
100
92
|
//# sourceMappingURL=visual-edit-plugin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visual-edit-plugin.js","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,uBAAuB;QAC7B,KAAK,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,aAAa;QAChD,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,KAAK;QACZ,kBAAkB,CAAC,IAAS;YAC1B,MAAM,cAAc,GAAG,+GAA+G,CAAC;YACvI,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,GAAG,SAAS,CAAC,CAAC;QAC7D,CAAC;QACD,SAAS,CAAC,IAAS,EAAE,EAAO;YAC1B,IAAI,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;gBACpE,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAChC,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;YAErC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE;oBACtB,UAAU,EAAE,QAAQ;oBACpB,OAAO,EAAE;wBACP,KAAK;wBACL,YAAY;wBACZ,mBAAmB;wBACnB,iBAAiB;wBACjB,kBAAkB;wBAClB,cAAc;wBACd,mBAAmB;wBACnB,qBAAqB;wBACrB,eAAe;wBACf,2BAA2B;wBAC3B,kBAAkB;wBAClB,iBAAiB;wBACjB,QAAQ;wBACR,sBAAsB;wBACtB,kBAAkB;qBACnB;iBACF,CAAC,CAAC;gBAEH,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACjB,MAAM,SAAS,GAAG,IAAI,YAAY,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;gBAEhD,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE;oBACpB,UAAU,CAAC,IAAI;wBACb,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;wBAC7B,IAAI,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC;4BAAE,OAAO;wBACxC,SAAS,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;oBAC1D,CAAC;iBACF,CAAC,CAAC;gBAEH,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE;oBACnC,OAAO,EAAE,KAAK;oBACd,OAAO,EAAE,KAAK;oBACd,WAAW,EAAE,IAAI;iBAClB,CAAC,CAAC;gBAEH,OAAO;oBACL,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,GAAG,EAAE,IAAI;iBACV,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;gBAC9D,OAAO;oBACL,IAAI,EAAE,IAAI;oBACV,GAAG,EAAE,IAAI;iBACV,CAAC;YACJ,CAAC;QACH,CAAC;KACQ,CAAC;AACd,CAAC;AAED,SAAS,eAAe,CAAC,EAAU;IACjC,MAAM,
|
|
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
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { Connect, Plugin, ViteDevServer } from "vite";
|
|
2
|
+
import type { ServerResponse } from "http";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Opt-in, loopback-gated dev-server endpoint that reports the last
|
|
6
|
+
* build / compile / HMR error observed since the dev server started.
|
|
7
|
+
*
|
|
8
|
+
* `GET /__build_status` responds `200` with a JSON body:
|
|
9
|
+
* { ok: boolean, error: <scrubbed error> | null }
|
|
10
|
+
*
|
|
11
|
+
* The endpoint is:
|
|
12
|
+
* - dev-only (`apply: "serve"`) — it never exists in a production build;
|
|
13
|
+
* - loopback-only — requests from non-loopback addresses fall through.
|
|
14
|
+
*
|
|
15
|
+
* Enablement (the `BASE44_BUILD_STATUS_ENABLED` env-var check) is decided by
|
|
16
|
+
* the caller in `index.ts`, which only adds this plugin to the array when the
|
|
17
|
+
* endpoint is wanted — so `configureServer` here is never even reached when
|
|
18
|
+
* the feature is off.
|
|
19
|
+
*
|
|
20
|
+
* The status is always reported in the body with HTTP `200`, even on error:
|
|
21
|
+
* a `500` here would trip the preview proxy's own error path.
|
|
22
|
+
*
|
|
23
|
+
* Cold-start caveat: errors thrown before any client has connected are not
|
|
24
|
+
* sent over the HMR channel, so a boot-time transform failure may briefly
|
|
25
|
+
* read `ok: true`. That window is covered server-side by the existing
|
|
26
|
+
* dev_server_error_classifier. This endpoint reports post-load
|
|
27
|
+
* HMR / compile errors.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const ENDPOINT = "/__build_status";
|
|
31
|
+
|
|
32
|
+
/** Max characters kept per scrubbed string field. */
|
|
33
|
+
const MAX_FIELD = 4000;
|
|
34
|
+
/** Max characters kept for the code frame. */
|
|
35
|
+
const MAX_FRAME = 2000;
|
|
36
|
+
|
|
37
|
+
type SendChannel = { send: (...args: unknown[]) => void };
|
|
38
|
+
|
|
39
|
+
const LOOPBACK_ADDRESSES = new Set([
|
|
40
|
+
"127.0.0.1",
|
|
41
|
+
"::1",
|
|
42
|
+
"::ffff:127.0.0.1",
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
function isLoopback(remoteAddress: string | undefined): boolean {
|
|
46
|
+
return remoteAddress !== undefined && LOOPBACK_ADDRESSES.has(remoteAddress);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function clip(value: unknown, max: number): string | undefined {
|
|
50
|
+
if (typeof value !== "string" || value.length === 0) return undefined;
|
|
51
|
+
return value.length > max ? `${value.slice(0, max)}… [truncated]` : value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Strip absolute filesystem paths down to their basename so directory
|
|
56
|
+
* structure never leaks. This is loopback-only, but the backend relays a
|
|
57
|
+
* trimmed form, so scrub before it leaves this process.
|
|
58
|
+
*/
|
|
59
|
+
function stripPaths(value: string): string {
|
|
60
|
+
return (
|
|
61
|
+
value
|
|
62
|
+
// Windows: C:\Users\foo\bar\file.ext -> file.ext
|
|
63
|
+
.replace(/[A-Za-z]:\\[^\s:*?"<>|]+/g, (m) => m.split(/[\\/]/).pop() ?? m)
|
|
64
|
+
// POSIX absolute: /Users/foo/bar/file.ext -> file.ext
|
|
65
|
+
.replace(/\/(?:[^\s:/]+\/)+[^\s:/]+/g, (m) => m.split("/").pop() ?? m)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function scrubString(value: unknown, max: number): string | undefined {
|
|
70
|
+
const clipped = clip(value, max);
|
|
71
|
+
return clipped === undefined ? undefined : stripPaths(clipped);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Produce a trimmed, path-stripped, size-capped view of an HMR error
|
|
76
|
+
* payload. Returns `null` when there is no error.
|
|
77
|
+
*/
|
|
78
|
+
export function scrub(err: unknown): Record<string, unknown> | null {
|
|
79
|
+
if (err === null || err === undefined) return null;
|
|
80
|
+
|
|
81
|
+
if (typeof err !== "object") {
|
|
82
|
+
const message = scrubString(String(err), MAX_FIELD);
|
|
83
|
+
return message === undefined ? null : { message };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const e = err as Record<string, unknown>;
|
|
87
|
+
const out: Record<string, unknown> = {};
|
|
88
|
+
|
|
89
|
+
const message = scrubString(e["message"], MAX_FIELD);
|
|
90
|
+
if (message !== undefined) out["message"] = message;
|
|
91
|
+
|
|
92
|
+
const stack = scrubString(e["stack"], MAX_FIELD);
|
|
93
|
+
if (stack !== undefined) out["stack"] = stack;
|
|
94
|
+
|
|
95
|
+
const frame = scrubString(e["frame"], MAX_FRAME);
|
|
96
|
+
if (frame !== undefined) out["frame"] = frame;
|
|
97
|
+
|
|
98
|
+
const id = scrubString(e["id"], MAX_FIELD);
|
|
99
|
+
if (id !== undefined) out["id"] = id;
|
|
100
|
+
|
|
101
|
+
const plugin = scrubString(e["plugin"], 200);
|
|
102
|
+
if (plugin !== undefined) out["plugin"] = plugin;
|
|
103
|
+
|
|
104
|
+
const loc = e["loc"];
|
|
105
|
+
if (loc && typeof loc === "object") {
|
|
106
|
+
const l = loc as Record<string, unknown>;
|
|
107
|
+
out["loc"] = {
|
|
108
|
+
file: scrubString(l["file"], MAX_FIELD),
|
|
109
|
+
line: typeof l["line"] === "number" ? l["line"] : undefined,
|
|
110
|
+
column: typeof l["column"] === "number" ? l["column"] : undefined,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Fall back to a stringified form if we recognised nothing useful.
|
|
115
|
+
if (Object.keys(out).length === 0) {
|
|
116
|
+
const message = scrubString(String(e), MAX_FIELD);
|
|
117
|
+
return message === undefined ? {} : { message };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function buildStatusPlugin(): Plugin {
|
|
124
|
+
let lastError: unknown = null;
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
name: "base44-build-status",
|
|
128
|
+
apply: "serve",
|
|
129
|
+
configureServer(server: ViteDevServer) {
|
|
130
|
+
try {
|
|
131
|
+
// Observe the HMR channel: an `error` payload records the last
|
|
132
|
+
// error; a successful `update` / `full-reload` clears it.
|
|
133
|
+
const ch = (server.hot ?? server.ws) as unknown as SendChannel | undefined;
|
|
134
|
+
if (ch && typeof ch.send === "function") {
|
|
135
|
+
const orig = ch.send.bind(ch);
|
|
136
|
+
ch.send = (payload: unknown, ...rest: unknown[]) => {
|
|
137
|
+
try {
|
|
138
|
+
const p = payload as { type?: string; err?: unknown } | undefined;
|
|
139
|
+
if (p?.type === "error") {
|
|
140
|
+
lastError = p.err ?? null;
|
|
141
|
+
} else if (p?.type === "update" || p?.type === "full-reload") {
|
|
142
|
+
lastError = null;
|
|
143
|
+
}
|
|
144
|
+
} catch {
|
|
145
|
+
// Never let bookkeeping break HMR delivery.
|
|
146
|
+
}
|
|
147
|
+
return orig(payload, ...rest);
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
server.middlewares.use(
|
|
152
|
+
ENDPOINT,
|
|
153
|
+
(
|
|
154
|
+
req: Connect.IncomingMessage,
|
|
155
|
+
res: ServerResponse,
|
|
156
|
+
next: Connect.NextFunction
|
|
157
|
+
) => {
|
|
158
|
+
try {
|
|
159
|
+
if (!isLoopback(req.socket?.remoteAddress)) {
|
|
160
|
+
next();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (req.method !== "GET") {
|
|
164
|
+
next();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const body = JSON.stringify({
|
|
169
|
+
ok: !lastError,
|
|
170
|
+
error: scrub(lastError),
|
|
171
|
+
});
|
|
172
|
+
// Health lives in the body — keep 200 even on error so the
|
|
173
|
+
// preview proxy doesn't treat this as a server failure.
|
|
174
|
+
res.statusCode = 200;
|
|
175
|
+
res.setHeader("Content-Type", "application/json");
|
|
176
|
+
res.setHeader("Cache-Control", "no-store");
|
|
177
|
+
res.end(body);
|
|
178
|
+
} catch {
|
|
179
|
+
// Fail open: a bug here must never break the dev server.
|
|
180
|
+
try {
|
|
181
|
+
next();
|
|
182
|
+
} catch {
|
|
183
|
+
// ignore
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
} catch {
|
|
189
|
+
// Fail open: setup errors must never break the dev server.
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { errorOverlayPlugin } from "./error-overlay-plugin.js";
|
|
|
4
4
|
import { visualEditPlugin } from "./visual-edit-plugin.js";
|
|
5
5
|
import { filterPackagesInProject } from "./utils.js";
|
|
6
6
|
import { htmlInjectionsPlugin } from "./html-injections-plugin.js";
|
|
7
|
+
import { buildStatusPlugin } from "./build-status-plugin.js";
|
|
7
8
|
|
|
8
9
|
const isRunningInSandbox = !!process.env.MODAL_SANDBOX_ID;
|
|
9
10
|
|
|
@@ -229,5 +230,12 @@ export default function vitePlugin(
|
|
|
229
230
|
: []),
|
|
230
231
|
// HTML injections - handles both dev (sandbox) and production injections
|
|
231
232
|
htmlInjectionsPlugin({ hmrNotifier, navigationNotifier, visualEditAgent, analyticsTracker }),
|
|
233
|
+
// Opt-in, loopback-gated dev-server build-status endpoint. Gated here on
|
|
234
|
+
// the BASE44_BUILD_STATUS_ENABLED env var so the generated app's
|
|
235
|
+
// vite.config.js doesn't need to change and the sub-plugin isn't even
|
|
236
|
+
// added to the pipeline when disabled. The endpoint itself is dev-only.
|
|
237
|
+
...(process.env.BASE44_BUILD_STATUS_ENABLED === "1"
|
|
238
|
+
? [buildStatusPlugin()]
|
|
239
|
+
: []),
|
|
232
240
|
];
|
|
233
241
|
}
|
|
@@ -14,7 +14,7 @@ It adds two HTML `data-*` attributes to every JSX element:
|
|
|
14
14
|
|
|
15
15
|
// AFTER (served to browser):
|
|
16
16
|
<Button
|
|
17
|
-
data-source-location="components/Button:42:8"
|
|
17
|
+
data-source-location="src/components/Button.jsx:42:8"
|
|
18
18
|
data-dynamic-content="false"
|
|
19
19
|
className="px-4"
|
|
20
20
|
>Click me</Button>
|
|
@@ -65,19 +65,18 @@ It adds two HTML `data-*` attributes to every JSX element:
|
|
|
65
65
|
|
|
66
66
|
### Step 1: Filename Extraction (lines 123-173)
|
|
67
67
|
|
|
68
|
-
Builds
|
|
68
|
+
Builds the canonical real source path (the path the backend and sandbox use) from the full file path:
|
|
69
69
|
|
|
70
70
|
```
|
|
71
|
-
/Users/dev/project/src/pages/About/index.tsx → "pages/About/index"
|
|
72
|
-
/Users/dev/project/src/components/ui/Button.tsx → "components/ui/Button"
|
|
73
|
-
/Users/dev/project/src/Layout.tsx → "Layout"
|
|
71
|
+
/Users/dev/project/src/pages/About/index.tsx → "src/pages/About/index.tsx"
|
|
72
|
+
/Users/dev/project/src/components/ui/Button.tsx → "src/components/ui/Button.tsx"
|
|
73
|
+
/Users/dev/project/src/Layout.tsx → "src/Layout.tsx"
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
**Rules:**
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
- File extensions are always stripped
|
|
77
|
+
- Anchors on the last `src/` segment and keeps everything from there, preserving nested structure
|
|
78
|
+
- The file extension is retained (`.jsx`/`.tsx`/`.js`/`.ts`)
|
|
79
|
+
- Files not under `src/` → just the filename (extension kept)
|
|
81
80
|
|
|
82
81
|
### Step 2: AST Parsing (lines 177-196)
|
|
83
82
|
|
|
@@ -260,7 +259,7 @@ The parent window (editor UI) and the sandbox iframe can't share a DOM. The `dat
|
|
|
260
259
|
```
|
|
261
260
|
Parent Window (Editor UI) iframe Sandbox (React App)
|
|
262
261
|
───────────────────────── ──────────────────────────
|
|
263
|
-
"Select element components/Button:42:8" → querySelector('[data-source-location="components/Button:42:8"]')
|
|
262
|
+
"Select element src/components/Button.jsx:42:8" → querySelector('[data-source-location="src/components/Button.jsx:42:8"]')
|
|
264
263
|
← "Element found: classes='px-4', isDynamic=false"
|
|
265
264
|
"Update classes to 'px-6 py-3'" → element.setAttribute("class", "px-6 py-3")
|
|
266
265
|
```
|
|
@@ -305,7 +304,7 @@ The `transformIndexHtml` hook injects the Tailwind CSS CDN, enabling visual edit
|
|
|
305
304
|
│ │ React App (DOM) │ │
|
|
306
305
|
│ │ │ │
|
|
307
306
|
│ │ <div data-source-location= │ │
|
|
308
|
-
│ │ "pages/Home:10:4"
|
|
307
|
+
│ │ "src/pages/Home.jsx:10:4" │ │
|
|
309
308
|
│ │ data-dynamic-content="true"> │ │
|
|
310
309
|
│ │ {greeting} │ │
|
|
311
310
|
│ │ </div> │ │
|
|
@@ -81,30 +81,19 @@ export function visualEditPlugin() {
|
|
|
81
81
|
} as Plugin;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// Emit the canonical real path (src/pages/Home.jsx) the backend and sandbox use, NOT a
|
|
85
|
+
// legacy alias (pages/Home). The path is anchored on the last "src" segment with the
|
|
86
|
+
// extension retained, so consumers get the file's true sandbox path directly.
|
|
84
87
|
function extractFilename(id: string): string {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return
|
|
88
|
+
// Drop any Vite query suffix (e.g. "?v=...") and normalize separators.
|
|
89
|
+
const cleanId = id.split("?")[0]!.replace(/\\/g, "/");
|
|
90
|
+
const parts = cleanId.split("/");
|
|
91
|
+
// Anchor on the LAST "src" segment so a "src" elsewhere in the absolute path
|
|
92
|
+
// (e.g. a user's home dir) can't mis-anchor. base44 apps keep all source under src/.
|
|
93
|
+
const srcIndex = parts.lastIndexOf("src");
|
|
94
|
+
if (srcIndex >= 0) {
|
|
95
|
+
return parts.slice(srcIndex).join("/"); // src/pages/Home.jsx, src/Layout.jsx, src/components/ui/Card.tsx
|
|
93
96
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return stripExtension(lastPart);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function findSegmentIndex(parts: string[], segments: string[]): number {
|
|
100
|
-
for (const segment of segments) {
|
|
101
|
-
const idx = parts.findIndex((part) => part === segment);
|
|
102
|
-
if (idx >= 0) return idx;
|
|
103
|
-
}
|
|
104
|
-
return -1;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function stripExtension(filename: string): string {
|
|
108
|
-
const dotIndex = filename.indexOf(".");
|
|
109
|
-
return dotIndex >= 0 ? filename.substring(0, dotIndex) : filename;
|
|
97
|
+
// Fallback: file not under src/ — use the bare filename (extension kept).
|
|
98
|
+
return parts[parts.length - 1] ?? "";
|
|
110
99
|
}
|