@base44-preview/vite-plugin 1.0.45-pr.134.9175d68 → 1.0.46-pr.136.6f6c15f
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 +23 -0
- package/compat/workerd-enter-with.mjs +20 -0
- package/compat/workerd-ws.cjs +135 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +4 -0
- package/dist/utils.js.map +1 -1
- package/dist/visual-edit-plugin.d.ts.map +1 -1
- package/dist/visual-edit-plugin.js +9 -2
- package/dist/visual-edit-plugin.js.map +1 -1
- package/dist/workerd-compat-plugin.d.ts +21 -0
- package/dist/workerd-compat-plugin.d.ts.map +1 -0
- package/dist/workerd-compat-plugin.js +253 -0
- package/dist/workerd-compat-plugin.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +8 -6
- package/src/utils.ts +5 -0
- package/src/visual-edit-plugin.md +4 -3
- package/src/visual-edit-plugin.ts +8 -3
- package/src/workerd-compat-plugin.ts +300 -0
package/README.md
CHANGED
|
@@ -51,6 +51,29 @@ Dev and production are told apart by the Vite command (`serve` vs `build`), not
|
|
|
51
51
|
|
|
52
52
|
Outside a sandbox, only the core plugin runs (with optional API proxy support via `VITE_BASE44_APP_BASE_URL`).
|
|
53
53
|
|
|
54
|
+
### Cloudflare Workers compatibility
|
|
55
|
+
|
|
56
|
+
`base44-workerd-compat` adjusts only environments that resolve with the `workerd`
|
|
57
|
+
condition, which Cloudflare's Vite plugin uses for server code. The browser bundle
|
|
58
|
+
and Node-run servers are untouched. It covers both Vite's pipeline and the dev
|
|
59
|
+
dependency optimizer, for esbuild (Vite 7) and Rolldown (Vite 8):
|
|
60
|
+
|
|
61
|
+
- **`ws`** resolves to `compat/workerd-ws.cjs`, a `ws`-shaped client over workerd's
|
|
62
|
+
native WebSocket. The package's own browser build only throws. `WebSocketServer`
|
|
63
|
+
throws too: server code can open sockets but not listen.
|
|
64
|
+
- **`AsyncLocalStorage.enterWith()`**, which always throws in workerd, becomes a
|
|
65
|
+
no-op. A module that calls it first imports `compat/workerd-enter-with.mjs`. The
|
|
66
|
+
no-op is global to the isolate: any code that relies on `enterWith()` to
|
|
67
|
+
propagate state gets nothing, silently, instead of an exception. Use `run()`.
|
|
68
|
+
- **`createRequire()` in dev** gets its argument's query string dropped. Optimized
|
|
69
|
+
dependencies carry Vite's `?v=` query, which workerd rejects. Builds are left
|
|
70
|
+
byte-for-byte alone.
|
|
71
|
+
|
|
72
|
+
Stagehand 3 needs all three. `@base44/functions-compiler` applies the first two to
|
|
73
|
+
backend functions. Cloudflare's `nodejs_compat` flag does not cover any of them:
|
|
74
|
+
its `node:http` client cannot upgrade a connection, which `ws` needs, `enterWith()`
|
|
75
|
+
throws by design, and `createRequire()` rejects queries.
|
|
76
|
+
|
|
54
77
|
### Build-status endpoint
|
|
55
78
|
|
|
56
79
|
An opt-in, dev-only (`apply: "serve"`) endpoint that reports the last build /
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Imported ahead of any Cloudflare Workers server module that calls enterWith,
|
|
2
|
+
// by the base44-workerd-compat plugin. workerd defines
|
|
3
|
+
// AsyncLocalStorage.prototype.enterWith but makes it always throw; libraries such
|
|
4
|
+
// as Stagehand call it to seed a context they also pass explicitly, so a no-op
|
|
5
|
+
// keeps them working.
|
|
6
|
+
//
|
|
7
|
+
// The no-op is global: any caller in the isolate that relies on enterWith to
|
|
8
|
+
// propagate state gets nothing instead of an exception. Storing the value per
|
|
9
|
+
// instance instead would leak it across the concurrent requests an isolate
|
|
10
|
+
// serves, which is worse. A runtime whose enterWith works is left alone.
|
|
11
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
12
|
+
|
|
13
|
+
const native = AsyncLocalStorage.prototype.enterWith;
|
|
14
|
+
let works = false;
|
|
15
|
+
try {
|
|
16
|
+
native.call(new AsyncLocalStorage(), undefined);
|
|
17
|
+
works = true;
|
|
18
|
+
} catch {}
|
|
19
|
+
|
|
20
|
+
if (!works) AsyncLocalStorage.prototype.enterWith = function enterWith() {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Served for `ws` in Cloudflare Workers environments by the base44-workerd-compat
|
|
2
|
+
// plugin. The `ws` package's "browser" export is a stub that throws, and its Node
|
|
3
|
+
// build needs node:net; workerd has a native WebSocket client, so this is a
|
|
4
|
+
// `ws`-shaped client over it. Kept in step with @base44/functions-compiler's copy.
|
|
5
|
+
"use strict";
|
|
6
|
+
const CONNECTING = 0;
|
|
7
|
+
const OPEN = 1;
|
|
8
|
+
const CLOSING = 2;
|
|
9
|
+
const CLOSED = 3;
|
|
10
|
+
const toBuffer = (value) => (globalThis.Buffer ? globalThis.Buffer.from(value) : value);
|
|
11
|
+
// The WHATWG close() that workerd implements accepts fewer codes than `ws` does.
|
|
12
|
+
const isWhatwgCloseCode = (code) => code === 1000 || (code >= 3000 && code <= 4999);
|
|
13
|
+
const later = (fn, ...args) => queueMicrotask(() => fn(...args));
|
|
14
|
+
class WebSocket {
|
|
15
|
+
static CONNECTING = CONNECTING;
|
|
16
|
+
static OPEN = OPEN;
|
|
17
|
+
static CLOSING = CLOSING;
|
|
18
|
+
static CLOSED = CLOSED;
|
|
19
|
+
#socket;
|
|
20
|
+
#listeners = new Map();
|
|
21
|
+
#maxListeners = 10;
|
|
22
|
+
#terminated = false;
|
|
23
|
+
constructor(address, protocols) {
|
|
24
|
+
this.#socket = new globalThis.WebSocket(String(address), typeof protocols === "string" || Array.isArray(protocols) ? protocols : undefined);
|
|
25
|
+
try { this.#socket.binaryType = "arraybuffer"; } catch {}
|
|
26
|
+
this.#socket.addEventListener("open", () => this.emit("open"));
|
|
27
|
+
this.#socket.addEventListener("message", (event) => {
|
|
28
|
+
const isBinary = typeof event.data !== "string";
|
|
29
|
+
this.emit("message", toBuffer(isBinary ? new Uint8Array(event.data) : event.data), isBinary);
|
|
30
|
+
});
|
|
31
|
+
this.#socket.addEventListener("error", (event) => this.emit("error", event.error ?? new Error(event.message || "WebSocket error")));
|
|
32
|
+
this.#socket.addEventListener("close", (event) => {
|
|
33
|
+
if (!this.#terminated) this.emit("close", event.code, toBuffer(event.reason ?? ""));
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
get readyState() { return this.#terminated ? CLOSED : this.#socket.readyState; }
|
|
37
|
+
get url() { return this.#socket.url; }
|
|
38
|
+
get protocol() { return this.#socket.protocol; }
|
|
39
|
+
on(name, listener) { return this.#add(name, listener, false, false); }
|
|
40
|
+
addListener(name, listener) { return this.#add(name, listener, false, false); }
|
|
41
|
+
once(name, listener) { return this.#add(name, listener, true, false); }
|
|
42
|
+
prependListener(name, listener) { return this.#add(name, listener, false, true); }
|
|
43
|
+
prependOnceListener(name, listener) { return this.#add(name, listener, true, true); }
|
|
44
|
+
off(name, listener) { return this.removeListener(name, listener); }
|
|
45
|
+
removeListener(name, listener) {
|
|
46
|
+
const listeners = this.#listeners.get(name);
|
|
47
|
+
if (listeners) this.#listeners.set(name, listeners.filter((entry) => entry.listener !== listener));
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
removeAllListeners(name) {
|
|
51
|
+
if (name === undefined) this.#listeners.clear();
|
|
52
|
+
else this.#listeners.delete(name);
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
listeners(name) { return (this.#listeners.get(name) ?? []).map((entry) => entry.listener); }
|
|
56
|
+
listenerCount(name) { return this.#listeners.get(name)?.length ?? 0; }
|
|
57
|
+
eventNames() { return [...this.#listeners.keys()].filter((name) => this.listenerCount(name) > 0); }
|
|
58
|
+
setMaxListeners(count) { this.#maxListeners = count; return this; }
|
|
59
|
+
getMaxListeners() { return this.#maxListeners; }
|
|
60
|
+
emit(name, ...args) {
|
|
61
|
+
// Dispatch to a snapshot, as Node's EventEmitter does.
|
|
62
|
+
const listeners = this.#listeners.get(name) ?? [];
|
|
63
|
+
this.#listeners.set(name, listeners.filter((entry) => !entry.once));
|
|
64
|
+
for (const entry of listeners) entry.listener.apply(this, args);
|
|
65
|
+
return listeners.length > 0;
|
|
66
|
+
}
|
|
67
|
+
send(data, options, callback) {
|
|
68
|
+
const done = typeof options === "function" ? options : callback;
|
|
69
|
+
try {
|
|
70
|
+
this.#socket.send(data);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (!done) throw error;
|
|
73
|
+
later(done, error);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// `ws` reports completion asynchronously; so does this.
|
|
77
|
+
if (done) later(done);
|
|
78
|
+
}
|
|
79
|
+
// workerd answers the peer's pings itself but cannot send one. A keepalive
|
|
80
|
+
// loop waiting for "pong" would otherwise kill a healthy socket, so an open
|
|
81
|
+
// socket answers with one.
|
|
82
|
+
ping(data, mask, callback) {
|
|
83
|
+
const done = [data, mask, callback].find((arg) => typeof arg === "function");
|
|
84
|
+
if (this.readyState === OPEN) later(() => this.emit("pong", toBuffer("")));
|
|
85
|
+
if (done) later(done);
|
|
86
|
+
}
|
|
87
|
+
pong(data, mask, callback) {
|
|
88
|
+
const done = [data, mask, callback].find((arg) => typeof arg === "function");
|
|
89
|
+
if (done) later(done);
|
|
90
|
+
}
|
|
91
|
+
close(code, reason) {
|
|
92
|
+
try {
|
|
93
|
+
this.#socket.close(code, reason);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
// A code `ws` accepts but WHATWG close() rejects, such as 1001.
|
|
96
|
+
if (code === undefined || isWhatwgCloseCode(code)) throw error;
|
|
97
|
+
this.#socket.close(1000, reason);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// `ws` tears the connection down at once. workerd has no abrupt close, so the
|
|
101
|
+
// socket is closed normally and "close" is reported now, not on the reply.
|
|
102
|
+
terminate() {
|
|
103
|
+
if (this.#terminated || this.#socket.readyState === CLOSED) return;
|
|
104
|
+
this.#terminated = true;
|
|
105
|
+
try { this.#socket.close(); } catch {}
|
|
106
|
+
later(() => this.emit("close", 1006, toBuffer("")));
|
|
107
|
+
}
|
|
108
|
+
#add(name, listener, once, prepend) {
|
|
109
|
+
const listeners = this.#listeners.get(name) ?? [];
|
|
110
|
+
if (prepend) listeners.unshift({ listener, once });
|
|
111
|
+
else listeners.push({ listener, once });
|
|
112
|
+
this.#listeners.set(name, listeners);
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
Object.assign(WebSocket.prototype, { CONNECTING, OPEN, CLOSING, CLOSED });
|
|
117
|
+
const unavailable = (what) => new Error(`ws ${what} is not available in Cloudflare Workers server code, which only supports the ws WebSocket client.`);
|
|
118
|
+
class WebSocketServer {
|
|
119
|
+
constructor() { throw unavailable("WebSocketServer"); }
|
|
120
|
+
}
|
|
121
|
+
class Receiver {
|
|
122
|
+
constructor() { throw unavailable("Receiver"); }
|
|
123
|
+
}
|
|
124
|
+
class Sender {
|
|
125
|
+
constructor() { throw unavailable("Sender"); }
|
|
126
|
+
}
|
|
127
|
+
function createWebSocketStream() { throw unavailable("createWebSocketStream"); }
|
|
128
|
+
module.exports = WebSocket;
|
|
129
|
+
module.exports.WebSocket = WebSocket;
|
|
130
|
+
module.exports.WebSocketServer = WebSocketServer;
|
|
131
|
+
module.exports.Server = WebSocketServer;
|
|
132
|
+
module.exports.Receiver = Receiver;
|
|
133
|
+
module.exports.Sender = Sender;
|
|
134
|
+
module.exports.createWebSocketStream = createWebSocketStream;
|
|
135
|
+
module.exports.default = WebSocket;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,MAAM,CAAC;AAU/C,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAiBlD,eAAO,MAAM,uBAAuB,UAWnC,CAAC;AAeF,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,iBA2PP"}
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { loadEnv
|
|
2
|
+
import { loadEnv } from "vite";
|
|
3
3
|
import { errorOverlayPlugin } from "./error-overlay-plugin.js";
|
|
4
4
|
import { visualEditPlugin } from "./visual-edit-plugin.js";
|
|
5
|
-
import { filterPackagesInProject } from "./utils.js";
|
|
5
|
+
import { filterPackagesInProject, isVite8OrLater } from "./utils.js";
|
|
6
6
|
import { htmlInjectionsPlugin } from "./html-injections-plugin.js";
|
|
7
7
|
import { buildStatusPlugin } from "./build-status-plugin.js";
|
|
8
8
|
import { base44RuntimePlugin } from "./base44-runtime-plugin.js";
|
|
9
|
+
import { workerdCompatPlugin } from "./workerd-compat-plugin.js";
|
|
9
10
|
import { routeRequestLogPlugin } from "./route-request-log/vite-plugin.js";
|
|
10
11
|
const isRunningInSandbox = !!process.env.MODAL_SANDBOX_ID;
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// treat .js files as JSX, since app code routinely puts JSX in .js.
|
|
14
|
-
const isVite8OrLater = Number(viteVersion.split(".")[0]) >= 8;
|
|
12
|
+
// Both optimizer spellings mean the same thing here: treat .js files as JSX,
|
|
13
|
+
// since app code routinely puts JSX in .js.
|
|
15
14
|
const jsxInJsFiles = isVite8OrLater
|
|
16
15
|
? { rolldownOptions: { moduleTypes: { ".js": "jsx" } } }
|
|
17
16
|
: { esbuildOptions: { loader: { ".js": "jsx" } } };
|
|
@@ -228,6 +227,9 @@ export default function vitePlugin(opts = {}) {
|
|
|
228
227
|
// notices it. Ordering comes from its own `enforce: "pre"`, not from its
|
|
229
228
|
// position here — several tests index this array from the front.
|
|
230
229
|
base44RuntimePlugin(),
|
|
230
|
+
// `ws` and `AsyncLocalStorage.enterWith()` for Cloudflare Workers server code.
|
|
231
|
+
// Inert outside environments that resolve with the "workerd" condition.
|
|
232
|
+
workerdCompatPlugin(),
|
|
231
233
|
// Dev-only: tags a full-stack route's console output with the request's
|
|
232
234
|
// X-B44-Request-Id. Registered as a Nitro module (the `nitro` property)
|
|
233
235
|
// because Nitro runs dev routes in a worker whose console the Vite process
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,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,cAAc,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAG3E,MAAM,kBAAkB,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;AAE1D,6EAA6E;AAC7E,4CAA4C;AAC5C,MAAM,YAAY,GAAG,cAAc;IACjC,CAAC,CAAC,EAAE,eAAe,EAAE,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE;IACxD,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AAErD,0EAA0E;AAC1E,8EAA8E;AAC9E,+EAA+E;AAC/E,8EAA8E;AAC9E,4EAA4E;AAC5E,2EAA2E;AAC3E,yDAAyD;AACzD,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,OAAO;IACP,mBAAmB;IACnB,uBAAuB;IACvB,WAAW;IACX,kBAAkB;IAClB,kBAAkB;IAClB,eAAe;IACf,QAAQ;IACR,QAAQ;IACR,aAAa;CACd,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACzB,mHAAmH;IACnH,sDAAsD;IACtD,4EAA4E;IAC5E,iFAAiF;CAClF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,4BAA4B,GAAG;IACnC,8DAA8D;IAC9D,gEAAgE;IAChE,iEAAiE;CAClE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,CAAC,OAAO,UAAU,UAAU,CAChC,OAMI,EAAE;IAEN,MAAM,EACJ,gBAAgB,GAAG,KAAK,EACxB,WAAW,GAAG,KAAK,EACnB,kBAAkB,GAAG,KAAK,EAC1B,eAAe,GAAG,KAAK,EACvB,gBAAgB,GAAG,KAAK,GACzB,GAAG,IAAI,CAAC;IAET,OAAO;QACL;YACE,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;gBAC9B,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC;gBAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;gBAErD,IAAI,CAAC,kBAAkB,IAAI,OAAO,KAAK,OAAO,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC;oBAC1E,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;gBAC5C,CAAC;gBAED,OAAO;oBACL,OAAO,EAAE;wBACP,KAAK,EAAE;4BACL,IAAI,EAAE,OAAO;yBACd;qBACF;oBACD,GAAG,CAAC,gBAAgB;wBAClB,CAAC,CAAC;4BACE,MAAM,EAAE;gCACN,gCAAgC,EAAE,IAAI,CAAC,SAAS,CAC9C,GAAG,CAAC,kBAAkB,CACvB;gCACD,qCAAqC,EAAE,IAAI,CAAC,SAAS,CACnD,GAAG,CAAC,uBAAuB,CAC5B;6BACF;yBACF;wBACH,CAAC,CAAC,EAAE,CAAC;oBACP,GAAG,CAAC,kBAAkB;wBACpB,CAAC,CAAE;4BACC,QAAQ,EAAE,OAAO;4BACjB,MAAM,EAAE;gCACN,IAAI,EAAE,IAAI;gCACV,IAAI,EAAE,SAAS,EAAE,8CAA8C;gCAC/D,IAAI,EAAE,IAAI;gCACV,UAAU,EAAE,IAAI;gCAChB,oDAAoD;gCACpD,YAAY,EAAE,IAAI;gCAClB,EAAE,EAAE;oCACF,MAAM,EAAE,IAAI;oCACZ,6FAA6F;oCAC7F,IAAI,EAAE;wCACJ,MAAM;wCACN,QAAQ;wCACR,aAAa;wCACb,YAAY;wCACZ,8FAA8F;wCAC9F,sFAAsF;wCACtF,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,KAAK;qCACzD;iCACF;gCACD,+DAA+D;gCAC/D,iEAAiE;gCACjE,+DAA+D;gCAC/D,uCAAuC;gCACvC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe;oCAC7B,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,EAAE;oCAChD,CAAC,CAAC,EAAE,CAAC;gCACP,KAAK,EAAE;oCACL,gEAAgE;oCAChE,UAAU,EAAE,IAAI;oCAChB,QAAQ,EAAE,GAAG,EAAE,uCAAuC;oCACtD,yDAAyD;oCACzD,gBAAgB,EAAE;wCAChB,kBAAkB,EAAE,GAAG;wCACvB,YAAY,EAAE,EAAE;qCACjB;iCACF;6BACF;4BACD,KAAK,EAAE;gCACL,aAAa,EAAE;oCACb,MAAM,CAAC,OAAO,EAAE,IAAI;wCAClB,sCAAsC;wCACtC,IACE,OAAO,CAAC,IAAI,KAAK,mBAAmB;4CACpC,OAAO,CAAC,IAAI,KAAK,gBAAgB,EACjC,CAAC;4CACD,MAAM,IAAI,KAAK,CAAC,iBAAiB,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;wCACtD,CAAC;wCACD,iCAAiC;wCACjC,IAAI,CAAC,OAAO,CAAC,CAAC;oCAChB,CAAC;iCACF;6BACF;yBACsB;wBAC3B,CAAC,CAAC,CAAC,GAAG,EAAE;4BACJ,IAAI,GAAG,CAAC,wBAAwB,EAAE,CAAC;gCACjC,OAAO,CAAC,GAAG,CACT,mCAAmC,GAAG,CAAC,wBAAwB,EAAE,CAClE,CAAC;gCACF,OAAO;oCACL,MAAM,EAAE;wCACN,KAAK,EAAE;4CACL,MAAM,EAAE;gDACN,MAAM,EAAE,GAAG,CAAC,wBAAwB;gDACpC,YAAY,EAAE,IAAI;6CACnB;yCACF;qCACF;iCACF,CAAC;4BACJ,CAAC;4BACD,OAAO,CAAC,GAAG,CACT,OAAO,KAAK,OAAO;gCACjB,CAAC,CAAC,kBAAkB;gCACpB,CAAC,CAAC,+DAA+D,CACpE,CAAC;4BACF,OAAO,EAAE,CAAC;wBACZ,CAAC,CAAC,EAAE,CAAC;oBACT,YAAY,EAAE;wBACZ,GAAG,CAAC,kBAAkB;4BACpB,CAAC,CAAC;gCACE,OAAO,EAAE,uBAAuB,CAC9B,uBAAuB,EACvB,IAAI,CACL;6BACF;4BACH,CAAC,CAAC,EAAE,CAAC;wBACP,GAAG,YAAY;qBAChB;iBACF,CAAC;YACJ,CAAC;YACD,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO;gBACvC,yEAAyE;gBACzE,2EAA2E;gBAC3E,IAAI,gBAAgB,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBACrD,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,OAAO,CAC3C,MAAM,EACN,QAAQ,EACR,OAAO,CACR,CAAC;oBAEF,IAAI,kBAAkB,EAAE,CAAC;wBACvB,OAAO,kBAAkB,CAAC;oBAC5B,CAAC;oBAED,iFAAiF;oBACjF,uEAAuE;oBACvE,8CAA8C;oBAC9C,IACE,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC;wBACrC,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,EAClC,CAAC;wBACD,OAAO,IAAI,CAAC,OAAO,CACjB,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,cAAc,CAAC,EACjD,QAAQ,EACR,OAAO,CACR,CAAC;oBACJ,CAAC;oBAED,iEAAiE;oBACjE,IACE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC;wBAC5B,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;4BAC1C,MAAM,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,EAC7C,CAAC;wBACD,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;oBACzD,CAAC;oBAED,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;wBACjC,OAAO,IAAI,CAAC,OAAO,CACjB,yCAAyC,EACzC,QAAQ,EACR,OAAO,CACR,CAAC;oBACJ,CAAC;oBAED,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;wBAClC,OAAO,IAAI,CAAC,OAAO,CACjB,0CAA0C,EAC1C,QAAQ,EACR,OAAO,CACR,CAAC;oBACJ,CAAC;oBAED,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;wBACrC,OAAO,IAAI,CAAC,OAAO,CACjB,6CAA6C,EAC7C,QAAQ,EACR,OAAO,CACR,CAAC;oBACJ,CAAC;oBAED,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;wBAC/B,OAAO,IAAI,CAAC,OAAO,CACjB,uCAAuC,EACvC,QAAQ,EACR,OAAO,CACR,CAAC;oBACJ,CAAC;gBACH,CAAC;gBAED,OAAO,IAAI,CAAC;YACd,CAAC;SACQ;QACX,GAAG,CAAC,kBAAkB;YACpB,CAAC,CAAC;gBACE;oBACE,IAAI,EAAE,YAAY;oBAClB,eAAe,CAAC,MAAM;wBACpB,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;4BACxC,yBAAyB;4BACzB,GAAG,CAAC,SAAS,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;4BAC7C,GAAG,CAAC,SAAS,CAAC,yBAAyB,EAAE,oBAAoB,CAAC,CAAC;4BAC/D,IAAI,EAAE,CAAC;wBACT,CAAC,CAAC,CAAC;oBACL,CAAC;iBACQ;gBACX,kBAAkB,EAAE;gBACpB,gBAAgB,EAAE;aACnB;YACH,CAAC,CAAC,EAAE,CAAC;QACP,yEAAyE;QACzE,GAAG,oBAAoB,CAAC;YACtB,WAAW;YACX,kBAAkB;YAClB,eAAe;YACf,gBAAgB;YAChB,WAAW,EAAE,kBAAkB;SAChC,CAAC;QACF,yEAAyE;QACzE,iEAAiE;QACjE,sEAAsE;QACtE,wEAAwE;QACxE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,KAAK,GAAG;YACjD,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC;YACvB,CAAC,CAAC,EAAE,CAAC;QACP,yEAAyE;QACzE,2EAA2E;QAC3E,yEAAyE;QACzE,iEAAiE;QACjE,mBAAmB,EAAE;QACrB,+EAA+E;QAC/E,wEAAwE;QACxE,mBAAmB,EAAE;QACrB,wEAAwE;QACxE,wEAAwE;QACxE,2EAA2E;QAC3E,4EAA4E;QAC5E,qBAAqB,EAAE;KACxB,CAAC;AACJ,CAAC"}
|
package/dist/utils.d.ts
CHANGED
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,SAAyC,CAAC;AAcrE,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,EAAE,MAAM,GACX,MAAM,EAAE,CAmBV"}
|
package/dist/utils.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
|
+
import { version as viteVersion } from "vite";
|
|
4
|
+
// Vite 8 pre-bundles deps with Rolldown instead of esbuild and deprecates
|
|
5
|
+
// `optimizeDeps.esbuildOptions`.
|
|
6
|
+
export const isVite8OrLater = Number(viteVersion.split(".")[0]) >= 8;
|
|
3
7
|
/**
|
|
4
8
|
* The package that owns an optimizeDeps entry. Deep entries like
|
|
5
9
|
* `react-dom/client` are separate entries to Vite's optimizer but are declared
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AAE9C,0EAA0E;AAC1E,iCAAiC;AACjC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAErE;;;;GAIG;AACH,SAAS,aAAa,CAAC,KAAa;IAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAC1B,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAChC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,QAAkB,EAClB,IAAY;IAEZ,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YACjC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;QACvE,MAAM,OAAO,GAAG;YACd,GAAG,WAAW,CAAC,YAAY;YAC3B,GAAG,WAAW,CAAC,eAAe;YAC9B,GAAG,WAAW,CAAC,gBAAgB;SAChC,CAAC;QAEF,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;QAC5D,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visual-edit-plugin.d.ts","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,MAAM,EAAc,MAAM,MAAM,CAAC;AAK1D,wBAAgB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"visual-edit-plugin.d.ts","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,MAAM,EAAc,MAAM,MAAM,CAAC;AAK1D,wBAAgB,gBAAgB,IAoFzB,MAAM,CACZ"}
|
|
@@ -16,7 +16,6 @@ export function visualEditPlugin() {
|
|
|
16
16
|
// code that doesn't exist on disk.
|
|
17
17
|
apply: (config, env) => env.command === "serve" && config.mode === "development",
|
|
18
18
|
enforce: "pre",
|
|
19
|
-
order: "pre",
|
|
20
19
|
transformIndexHtml(html) {
|
|
21
20
|
const tailwindScript = ` <!-- Tailwind CSS CDN for visual editing -->\n <script src="${TAILWIND_CDN_SRC}"></script>\n `;
|
|
22
21
|
return html.replace("</head>", tailwindScript + "</head>");
|
|
@@ -25,8 +24,16 @@ export function visualEditPlugin() {
|
|
|
25
24
|
if (id.includes("node_modules") || id.includes("visual-edit-agent")) {
|
|
26
25
|
return null;
|
|
27
26
|
}
|
|
28
|
-
|
|
27
|
+
const [idPath = "", idQuery] = id.split("?");
|
|
28
|
+
if (!/\.(jsx?|tsx?)$/.test(idPath))
|
|
29
29
|
return null;
|
|
30
|
+
// TanStack Router serves route components as `route.jsx?tsr-split=...` modules; other
|
|
31
|
+
// queries (?raw, ?url, ?worker) must not be transformed. Tag lines stay real only while
|
|
32
|
+
// base44() precedes tanstackStart() in vite.config, so this runs before the splitter.
|
|
33
|
+
if (idQuery) {
|
|
34
|
+
const params = new URLSearchParams(idQuery);
|
|
35
|
+
if (!params.has("tsr-split") && !params.has("tsr-shared"))
|
|
36
|
+
return null;
|
|
30
37
|
}
|
|
31
38
|
const filename = extractFilename(id);
|
|
32
39
|
try {
|
|
@@ -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;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,uBAAuB;QAC7B,mEAAmE;QACnE,sEAAsE;QACtE,oEAAoE;QACpE,sEAAsE;QACtE,oEAAoE;QACpE,mCAAmC;QACnC,KAAK,EAAE,CAAC,MAAkB,EAAE,GAAc,EAAE,EAAE,CAC5C,GAAG,CAAC,OAAO,KAAK,OAAO,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa;QAC1D,OAAO,EAAE,KAAK;QACd,
|
|
1
|
+
{"version":3,"file":"visual-edit-plugin.js","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
export declare const WORKERD_WS_ADAPTER: string;
|
|
3
|
+
export declare const WORKERD_ENTER_WITH_PATCH: string;
|
|
4
|
+
export interface PatchOptions {
|
|
5
|
+
/** Also route `createRequire` calls through the query-dropping wrapper. */
|
|
6
|
+
createRequire: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Patches a workerd server module: imports the enterWith no-op ahead of a module
|
|
10
|
+
* that calls `.enterWith()`, and, with `createRequire`, routes node:module's
|
|
11
|
+
* `createRequire` through a wrapper that drops the id's query. Code is inserted
|
|
12
|
+
* on the first line after any shebang or directive prologue, so strict mode
|
|
13
|
+
* holds and line numbers stay put. Returns null when nothing applies.
|
|
14
|
+
*/
|
|
15
|
+
export declare function patchServerModule(code: string, id: string, options: PatchOptions): string | null;
|
|
16
|
+
export interface WorkerdCompatOptions {
|
|
17
|
+
/** Which dependency optimizer to hook. Defaults to the installed Vite's. */
|
|
18
|
+
rolldown?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function workerdCompatPlugin({ rolldown }?: WorkerdCompatOptions): Plugin;
|
|
21
|
+
//# sourceMappingURL=workerd-compat-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerd-compat-plugin.d.ts","sourceRoot":"","sources":["../src/workerd-compat-plugin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAsB,MAAM,EAAE,MAAM,MAAM,CAAC;AA4BvD,eAAO,MAAM,kBAAkB,QAA+B,CAAC;AAC/D,eAAO,MAAM,wBAAwB,QAAuC,CAAC;AA8G7E,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,aAAa,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAiEhG;AAoDD,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,QAAyB,EAAE,GAAE,oBAAyB,GAAG,MAAM,CAqBpG"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { parse } from "@babel/parser";
|
|
4
|
+
import { default as traverse } from "@babel/traverse";
|
|
5
|
+
import { normalizePath } from "vite";
|
|
6
|
+
import { isVite8OrLater } from "./utils.js";
|
|
7
|
+
/**
|
|
8
|
+
* Closes three workerd gaps that break Node-targeted npm clients (Stagehand among
|
|
9
|
+
* them) in full-stack server code. @base44/functions-compiler covers the first
|
|
10
|
+
* two for backend functions:
|
|
11
|
+
*
|
|
12
|
+
* - `ws`: its exports map sends the "browser" condition (which Cloudflare's Vite
|
|
13
|
+
* plugin resolves with) to a stub that throws on use. It is served as a
|
|
14
|
+
* `ws`-shaped client over workerd's native WebSocket.
|
|
15
|
+
* - `AsyncLocalStorage.enterWith()`: workerd omits it. A module that calls it
|
|
16
|
+
* first imports a patch that makes it a no-op. Builtins never reach plugin
|
|
17
|
+
* resolution in Vite 8, so `async_hooks` itself cannot be swapped.
|
|
18
|
+
* - `createRequire()` in dev: optimized dependencies carry a `?v=` query in
|
|
19
|
+
* their ids, and workerd rejects a `createRequire` path with a query. Calls
|
|
20
|
+
* are routed through a wrapper that drops it. Builds have no query, so their
|
|
21
|
+
* bytes are left alone.
|
|
22
|
+
*
|
|
23
|
+
* Only environments that resolve with the "workerd" condition are touched, so
|
|
24
|
+
* the browser bundle and a Node-run server keep the real modules.
|
|
25
|
+
*/
|
|
26
|
+
// Vite ids use forward slashes on every platform, so these must too.
|
|
27
|
+
const compatFile = (name) => normalizePath(fileURLToPath(new URL(`../compat/${name}`, import.meta.url)));
|
|
28
|
+
export const WORKERD_WS_ADAPTER = compatFile("workerd-ws.cjs");
|
|
29
|
+
export const WORKERD_ENTER_WITH_PATCH = compatFile("workerd-enter-with.mjs");
|
|
30
|
+
const isWorkerd = (conditions) => !!conditions?.includes("workerd");
|
|
31
|
+
// `ws` exports only its root, so a deep import never resolves to begin with.
|
|
32
|
+
const redirect = (id) => (id === "ws" ? WORKERD_WS_ADAPTER : null);
|
|
33
|
+
// JSON, CSS and the like reach a "pre" transform raw. Virtual ids often have no
|
|
34
|
+
// extension at all, and are JavaScript.
|
|
35
|
+
const NON_SCRIPT_FILE = /\.(?![cm]?[jt]sx?$)[a-z0-9]+$/i;
|
|
36
|
+
const NODE_MODULE = new Set(["module", "node:module"]);
|
|
37
|
+
const CREATE_REQUIRE_IMPL = "__b44CreateRequireImpl";
|
|
38
|
+
const CREATE_REQUIRE = "__b44CreateRequire";
|
|
39
|
+
// @babel/traverse is CommonJS: Node's ESM interop nests the function under
|
|
40
|
+
// `default`, while bundlers and Vitest hand it over directly.
|
|
41
|
+
const traverseAst = (traverse.default ??
|
|
42
|
+
traverse);
|
|
43
|
+
const pathOf = (id) => id.split("?")[0] ?? id;
|
|
44
|
+
function parserPlugins(file) {
|
|
45
|
+
if (/\.tsx$/.test(file))
|
|
46
|
+
return [["typescript", "jsx"]];
|
|
47
|
+
if (/\.[cm]?ts$/.test(file))
|
|
48
|
+
return [["typescript"]];
|
|
49
|
+
// Apps routinely put JSX in .js, but JSX parsing can misread plain JS, so it
|
|
50
|
+
// is only the fallback.
|
|
51
|
+
return [[], ["jsx"]];
|
|
52
|
+
}
|
|
53
|
+
function parseModule(code, file) {
|
|
54
|
+
for (const plugins of parserPlugins(file)) {
|
|
55
|
+
try {
|
|
56
|
+
return parse(code, {
|
|
57
|
+
// A module is ESM only if it has ESM syntax: text that merely looks
|
|
58
|
+
// like an import, inside a string or comment, does not count.
|
|
59
|
+
sourceType: /\.c[jt]s$/.test(file) ? "script" : /\.m[jt]s$/.test(file) ? "module" : "unambiguous",
|
|
60
|
+
allowReturnOutsideFunction: true,
|
|
61
|
+
allowAwaitOutsideFunction: true,
|
|
62
|
+
plugins,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Try the next plugin set.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const isRequireOfNodeModule = (node) => node?.type === "CallExpression" &&
|
|
72
|
+
node.callee.type === "Identifier" &&
|
|
73
|
+
node.callee.name === "require" &&
|
|
74
|
+
node.arguments[0]?.type === "StringLiteral" &&
|
|
75
|
+
NODE_MODULE.has(node.arguments[0].value);
|
|
76
|
+
const keyName = (key) => key.type === "Identifier" ? key.name : key.type === "StringLiteral" ? key.value : undefined;
|
|
77
|
+
/** Whether `name` at `path` is node:module's createRequire, under any alias. */
|
|
78
|
+
function bindsCreateRequire(path, name) {
|
|
79
|
+
const binding = path.scope.getBinding(name);
|
|
80
|
+
if (!binding)
|
|
81
|
+
return false;
|
|
82
|
+
const decl = binding.path.node;
|
|
83
|
+
const parent = binding.path.parent;
|
|
84
|
+
if (decl.type === "ImportSpecifier") {
|
|
85
|
+
return (parent.type === "ImportDeclaration" &&
|
|
86
|
+
NODE_MODULE.has(parent.source.value) &&
|
|
87
|
+
keyName(decl.imported) === "createRequire");
|
|
88
|
+
}
|
|
89
|
+
// const { createRequire } = require("node:module")
|
|
90
|
+
if (decl.type === "VariableDeclarator" && decl.id.type === "ObjectPattern" && isRequireOfNodeModule(decl.init)) {
|
|
91
|
+
return decl.id.properties.some((prop) => prop.type === "ObjectProperty" &&
|
|
92
|
+
keyName(prop.key) === "createRequire" &&
|
|
93
|
+
prop.value.type === "Identifier" &&
|
|
94
|
+
prop.value.name === name);
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
/** Whether `name` at `path` is node:module itself, as a default, namespace or required binding. */
|
|
99
|
+
function bindsNodeModule(path, name) {
|
|
100
|
+
const binding = path.scope.getBinding(name);
|
|
101
|
+
if (!binding)
|
|
102
|
+
return false;
|
|
103
|
+
const decl = binding.path.node;
|
|
104
|
+
const parent = binding.path.parent;
|
|
105
|
+
if (decl.type === "ImportDefaultSpecifier" || decl.type === "ImportNamespaceSpecifier") {
|
|
106
|
+
return parent.type === "ImportDeclaration" && NODE_MODULE.has(parent.source.value);
|
|
107
|
+
}
|
|
108
|
+
return decl.type === "VariableDeclarator" && decl.id.type === "Identifier" && isRequireOfNodeModule(decl.init);
|
|
109
|
+
}
|
|
110
|
+
/** The callee of a call that reaches node:module's createRequire, or null. */
|
|
111
|
+
function createRequireCallee(path) {
|
|
112
|
+
const callee = path.node.callee;
|
|
113
|
+
if (callee.type === "Identifier") {
|
|
114
|
+
return bindsCreateRequire(path, callee.name) ? callee : null;
|
|
115
|
+
}
|
|
116
|
+
if (callee.type !== "MemberExpression" || callee.computed || keyName(callee.property) !== "createRequire") {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const object = callee.object;
|
|
120
|
+
if (object.type === "Identifier")
|
|
121
|
+
return bindsNodeModule(path, object.name) ? callee : null;
|
|
122
|
+
return isRequireOfNodeModule(object) ? callee : null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Patches a workerd server module: imports the enterWith no-op ahead of a module
|
|
126
|
+
* that calls `.enterWith()`, and, with `createRequire`, routes node:module's
|
|
127
|
+
* `createRequire` through a wrapper that drops the id's query. Code is inserted
|
|
128
|
+
* on the first line after any shebang or directive prologue, so strict mode
|
|
129
|
+
* holds and line numbers stay put. Returns null when nothing applies.
|
|
130
|
+
*/
|
|
131
|
+
export function patchServerModule(code, id, options) {
|
|
132
|
+
const file = pathOf(id);
|
|
133
|
+
if (NON_SCRIPT_FILE.test(file.slice(file.lastIndexOf("/") + 1)) || file === WORKERD_ENTER_WITH_PATCH)
|
|
134
|
+
return null;
|
|
135
|
+
// The AST makes the call; this only skips files that cannot contain one,
|
|
136
|
+
// including calls a formatter split across lines.
|
|
137
|
+
const mayEnterWith = code.includes("enterWith");
|
|
138
|
+
const mayCreateRequire = options.createRequire && code.includes("createRequire") && !code.includes(CREATE_REQUIRE_IMPL);
|
|
139
|
+
if (!mayEnterWith && !mayCreateRequire)
|
|
140
|
+
return null;
|
|
141
|
+
const ast = parseModule(code, file);
|
|
142
|
+
if (!ast)
|
|
143
|
+
return null;
|
|
144
|
+
let callsEnterWith = false;
|
|
145
|
+
const callees = [];
|
|
146
|
+
const visit = (path) => {
|
|
147
|
+
const callee = path.node.callee;
|
|
148
|
+
if (mayEnterWith &&
|
|
149
|
+
(callee.type === "MemberExpression" || callee.type === "OptionalMemberExpression") &&
|
|
150
|
+
!callee.computed &&
|
|
151
|
+
keyName(callee.property) === "enterWith") {
|
|
152
|
+
callsEnterWith = true;
|
|
153
|
+
}
|
|
154
|
+
const target = mayCreateRequire ? createRequireCallee(path) : null;
|
|
155
|
+
if (target)
|
|
156
|
+
callees.push(target);
|
|
157
|
+
};
|
|
158
|
+
traverseAst(ast, { CallExpression: visit, OptionalCallExpression: visit });
|
|
159
|
+
if (!callsEnterWith && callees.length === 0)
|
|
160
|
+
return null;
|
|
161
|
+
const isEsm = ast.program.sourceType === "module";
|
|
162
|
+
let prefix = "";
|
|
163
|
+
if (callsEnterWith) {
|
|
164
|
+
const patch = JSON.stringify(WORKERD_ENTER_WITH_PATCH);
|
|
165
|
+
// An ESM import in a CommonJS module would turn it into ESM; require it there.
|
|
166
|
+
prefix += isEsm ? `import ${patch};` : `require(${patch});`;
|
|
167
|
+
}
|
|
168
|
+
if (callees.length > 0) {
|
|
169
|
+
prefix += isEsm
|
|
170
|
+
? `import { createRequire as ${CREATE_REQUIRE_IMPL} } from "node:module";`
|
|
171
|
+
: `const { createRequire: ${CREATE_REQUIRE_IMPL} } = require("node:module");`;
|
|
172
|
+
prefix += `const ${CREATE_REQUIRE} = (path) => ${CREATE_REQUIRE_IMPL}(String(path).replace(/[?#].*$/, ""));`;
|
|
173
|
+
}
|
|
174
|
+
// A shebang line cannot be followed on the same line, so the code goes at the
|
|
175
|
+
// start of the next one.
|
|
176
|
+
const { interpreter, directives } = ast.program;
|
|
177
|
+
let afterInterpreter = 0;
|
|
178
|
+
if (interpreter) {
|
|
179
|
+
const newline = code.indexOf("\n", interpreter.end);
|
|
180
|
+
if (newline === -1)
|
|
181
|
+
prefix = `\n${prefix}`;
|
|
182
|
+
afterInterpreter = newline === -1 ? code.length : newline + 1;
|
|
183
|
+
}
|
|
184
|
+
const insertAt = Math.max(afterInterpreter, ...directives.map((d) => d.end));
|
|
185
|
+
// A directive may end without a semicolon, and code cannot follow it directly.
|
|
186
|
+
if (insertAt > afterInterpreter && code[insertAt - 1] !== ";")
|
|
187
|
+
prefix = `;${prefix}`;
|
|
188
|
+
const edits = [
|
|
189
|
+
{ start: insertAt, end: insertAt, text: prefix },
|
|
190
|
+
...callees.map((node) => ({ start: node.start, end: node.end, text: CREATE_REQUIRE })),
|
|
191
|
+
].sort((a, b) => b.start - a.start);
|
|
192
|
+
let out = code;
|
|
193
|
+
for (const edit of edits)
|
|
194
|
+
out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
const devTransform = (code, id) => patchServerModule(code, id, { createRequire: true });
|
|
198
|
+
// Every edit keeps lines in place, so the input's source map still holds.
|
|
199
|
+
const transformResult = (code) => (code === null ? null : { code, map: null });
|
|
200
|
+
// Dev pre-bundles dependencies without running Vite plugins, so the dependency
|
|
201
|
+
// optimizer, which only runs in dev, needs its own copy. Each copy checks the
|
|
202
|
+
// build's conditions itself: Cloudflare's plugin sets them after ours has run.
|
|
203
|
+
function esbuildOptimizerPlugin() {
|
|
204
|
+
return {
|
|
205
|
+
name: "base44-workerd-compat",
|
|
206
|
+
setup(build) {
|
|
207
|
+
if (!isWorkerd(build.initialOptions.conditions))
|
|
208
|
+
return;
|
|
209
|
+
build.onResolve({ filter: /^ws$/ }, () => ({ path: WORKERD_WS_ADAPTER }));
|
|
210
|
+
// Other namespaces hold virtual modules with no file to read.
|
|
211
|
+
build.onLoad({ filter: /\.[cm]?js$/, namespace: "file" }, async (args) => {
|
|
212
|
+
const code = devTransform(await readFile(args.path, "utf8"), args.path);
|
|
213
|
+
return code === null ? undefined : { contents: code, loader: "js" };
|
|
214
|
+
});
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function rolldownOptimizerPlugin() {
|
|
219
|
+
let active = false;
|
|
220
|
+
return {
|
|
221
|
+
name: "base44-workerd-compat",
|
|
222
|
+
options(options) {
|
|
223
|
+
active = isWorkerd(options.resolve?.conditionNames);
|
|
224
|
+
},
|
|
225
|
+
resolveId: (id) => (active ? redirect(id) : null),
|
|
226
|
+
transform: (code, id) => (active ? transformResult(devTransform(code, id)) : null),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const OPTIMIZED_DEP = /[\\/]node_modules[\\/]\.vite[\\/]/;
|
|
230
|
+
export function workerdCompatPlugin({ rolldown = isVite8OrLater } = {}) {
|
|
231
|
+
return {
|
|
232
|
+
name: "base44-workerd-compat",
|
|
233
|
+
enforce: "pre",
|
|
234
|
+
applyToEnvironment: (environment) => isWorkerd(environment.config.resolve.conditions),
|
|
235
|
+
configEnvironment(name) {
|
|
236
|
+
if (name === "client")
|
|
237
|
+
return;
|
|
238
|
+
const optimizeDeps = rolldown
|
|
239
|
+
? { rolldownOptions: { plugins: [rolldownOptimizerPlugin()] } }
|
|
240
|
+
: { esbuildOptions: { plugins: [esbuildOptimizerPlugin()] } };
|
|
241
|
+
// Vite 7's types do not know `rolldownOptions`, hence the cast.
|
|
242
|
+
return { optimizeDeps };
|
|
243
|
+
},
|
|
244
|
+
resolveId: (id) => redirect(id),
|
|
245
|
+
transform(code, id) {
|
|
246
|
+
// The optimizer already patched its output.
|
|
247
|
+
if (OPTIMIZED_DEP.test(id))
|
|
248
|
+
return null;
|
|
249
|
+
return transformResult(patchServerModule(code, id, { createRequire: this.environment.mode === "dev" }));
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=workerd-compat-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerd-compat-plugin.js","sourceRoot":"","sources":["../src/workerd-compat-plugin.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import type { Plugin, UserConfig } from "vite";
|
|
3
|
-
import { loadEnv
|
|
3
|
+
import { loadEnv } from "vite";
|
|
4
4
|
import { errorOverlayPlugin } from "./error-overlay-plugin.js";
|
|
5
5
|
import { visualEditPlugin } from "./visual-edit-plugin.js";
|
|
6
|
-
import { filterPackagesInProject } from "./utils.js";
|
|
6
|
+
import { filterPackagesInProject, isVite8OrLater } from "./utils.js";
|
|
7
7
|
import { htmlInjectionsPlugin } from "./html-injections-plugin.js";
|
|
8
8
|
import { buildStatusPlugin } from "./build-status-plugin.js";
|
|
9
9
|
import { base44RuntimePlugin } from "./base44-runtime-plugin.js";
|
|
10
|
+
import { workerdCompatPlugin } from "./workerd-compat-plugin.js";
|
|
10
11
|
import { routeRequestLogPlugin } from "./route-request-log/vite-plugin.js";
|
|
11
12
|
export type { DocumentScript } from "./consts.js";
|
|
12
13
|
|
|
13
14
|
const isRunningInSandbox = !!process.env.MODAL_SANDBOX_ID;
|
|
14
15
|
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// treat .js files as JSX, since app code routinely puts JSX in .js.
|
|
18
|
-
const isVite8OrLater = Number(viteVersion.split(".")[0]) >= 8;
|
|
16
|
+
// Both optimizer spellings mean the same thing here: treat .js files as JSX,
|
|
17
|
+
// since app code routinely puts JSX in .js.
|
|
19
18
|
const jsxInJsFiles = isVite8OrLater
|
|
20
19
|
? { rolldownOptions: { moduleTypes: { ".js": "jsx" } } }
|
|
21
20
|
: { esbuildOptions: { loader: { ".js": "jsx" } } };
|
|
@@ -302,6 +301,9 @@ export default function vitePlugin(
|
|
|
302
301
|
// notices it. Ordering comes from its own `enforce: "pre"`, not from its
|
|
303
302
|
// position here — several tests index this array from the front.
|
|
304
303
|
base44RuntimePlugin(),
|
|
304
|
+
// `ws` and `AsyncLocalStorage.enterWith()` for Cloudflare Workers server code.
|
|
305
|
+
// Inert outside environments that resolve with the "workerd" condition.
|
|
306
|
+
workerdCompatPlugin(),
|
|
305
307
|
// Dev-only: tags a full-stack route's console output with the request's
|
|
306
308
|
// X-B44-Request-Id. Registered as a Nitro module (the `nitro` property)
|
|
307
309
|
// because Nitro runs dev routes in a worker whose console the Vite process
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
|
+
import { version as viteVersion } from "vite";
|
|
4
|
+
|
|
5
|
+
// Vite 8 pre-bundles deps with Rolldown instead of esbuild and deprecates
|
|
6
|
+
// `optimizeDeps.esbuildOptions`.
|
|
7
|
+
export const isVite8OrLater = Number(viteVersion.split(".")[0]) >= 8;
|
|
3
8
|
|
|
4
9
|
/**
|
|
5
10
|
* The package that owns an optimizeDeps entry. Deep entries like
|
|
@@ -30,7 +30,8 @@ It adds two HTML `data-*` attributes to every JSX element:
|
|
|
30
30
|
|-----------|-------|-----|
|
|
31
31
|
| `MODAL_SANDBOX_ID` env var is set | `index.ts:8` | Only runs inside Modal sandbox containers |
|
|
32
32
|
| Dev **server** in `"development"` mode | `apply: (config, env) => env.command === "serve" && config.mode === "development"` | The injected `data-*` markers are only consumed by the visual-edit agent in the live preview, which the dev server serves. Builds never use them — including `vite build --mode development` (the platform's validation build), where instrumenting user code once produced compile errors on code that wasn't on disk |
|
|
33
|
-
| File is `.js`, `.jsx`, `.ts`, or `.tsx` |
|
|
33
|
+
| File is `.js`, `.jsx`, `.ts`, or `.tsx` | `/\.(jsx?\|tsx?)$/.test(idPath)` | Only JSX-capable files |
|
|
34
|
+
| No query, or a `tsr-split` / `tsr-shared` key | `URLSearchParams(idQuery).has(...)` | TanStack Router serves route components as `route.jsx?tsr-split=component`; other queries (`?raw`, `?url`, `?worker`) must not be transformed. Lines stay accurate only while `base44()` precedes `tanstackStart()` in `vite.config`, so this runs before the splitter rewrites the file |
|
|
34
35
|
| File is NOT in `node_modules` | `id.includes("node_modules")` | Skip third-party code |
|
|
35
36
|
| File is NOT `visual-edit-agent` | `id.includes("visual-edit-agent")` | Don't instrument the agent itself |
|
|
36
37
|
|
|
@@ -57,8 +58,8 @@ It adds two HTML `data-*` attributes to every JSX element:
|
|
|
57
58
|
|
|
58
59
|
### Plugin Ordering
|
|
59
60
|
|
|
60
|
-
- `enforce: "pre"`
|
|
61
|
-
-
|
|
61
|
+
- `enforce: "pre"` runs this before all `normal`/`post` transforms. Among other `enforce: "pre"` plugins, array position in `vite.config` decides, so `base44()` must be listed before `tanstackStart()`: TanStack's code splitter is also `pre`, and tagging its output would point `data-source-location` at the wrong lines
|
|
62
|
+
- It also relies on Vite loading a queried id (`route.jsx?tsr-split=component`) from disk, so the split module's transform input is the original file, not the splitter's output. Both ids then tag identically, which keeps SSR and client hydration in agreement. Tags must come from raw JSX: tagging a rewritten file points `data-source-location` at generated lines
|
|
62
63
|
|
|
63
64
|
---
|
|
64
65
|
|
|
@@ -19,7 +19,6 @@ export function visualEditPlugin() {
|
|
|
19
19
|
apply: (config: UserConfig, env: ConfigEnv) =>
|
|
20
20
|
env.command === "serve" && config.mode === "development",
|
|
21
21
|
enforce: "pre",
|
|
22
|
-
order: "pre",
|
|
23
22
|
transformIndexHtml(html: any) {
|
|
24
23
|
const tailwindScript = ` <!-- Tailwind CSS CDN for visual editing -->\n <script src="${TAILWIND_CDN_SRC}"></script>\n `;
|
|
25
24
|
return html.replace("</head>", tailwindScript + "</head>");
|
|
@@ -29,8 +28,14 @@ export function visualEditPlugin() {
|
|
|
29
28
|
return null;
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const [idPath = "", idQuery] = id.split("?");
|
|
32
|
+
if (!/\.(jsx?|tsx?)$/.test(idPath)) return null;
|
|
33
|
+
// TanStack Router serves route components as `route.jsx?tsr-split=...` modules; other
|
|
34
|
+
// queries (?raw, ?url, ?worker) must not be transformed. Tag lines stay real only while
|
|
35
|
+
// base44() precedes tanstackStart() in vite.config, so this runs before the splitter.
|
|
36
|
+
if (idQuery) {
|
|
37
|
+
const params = new URLSearchParams(idQuery);
|
|
38
|
+
if (!params.has("tsr-split") && !params.has("tsr-shared")) return null;
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
const filename = extractFilename(id);
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { parse, type ParserPlugin } from "@babel/parser";
|
|
4
|
+
import { default as traverse, type NodePath } from "@babel/traverse";
|
|
5
|
+
import type * as t from "@babel/types";
|
|
6
|
+
import type { EnvironmentOptions, Plugin } from "vite";
|
|
7
|
+
import { normalizePath } from "vite";
|
|
8
|
+
import { isVite8OrLater } from "./utils.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Closes three workerd gaps that break Node-targeted npm clients (Stagehand among
|
|
12
|
+
* them) in full-stack server code. @base44/functions-compiler covers the first
|
|
13
|
+
* two for backend functions:
|
|
14
|
+
*
|
|
15
|
+
* - `ws`: its exports map sends the "browser" condition (which Cloudflare's Vite
|
|
16
|
+
* plugin resolves with) to a stub that throws on use. It is served as a
|
|
17
|
+
* `ws`-shaped client over workerd's native WebSocket.
|
|
18
|
+
* - `AsyncLocalStorage.enterWith()`: workerd omits it. A module that calls it
|
|
19
|
+
* first imports a patch that makes it a no-op. Builtins never reach plugin
|
|
20
|
+
* resolution in Vite 8, so `async_hooks` itself cannot be swapped.
|
|
21
|
+
* - `createRequire()` in dev: optimized dependencies carry a `?v=` query in
|
|
22
|
+
* their ids, and workerd rejects a `createRequire` path with a query. Calls
|
|
23
|
+
* are routed through a wrapper that drops it. Builds have no query, so their
|
|
24
|
+
* bytes are left alone.
|
|
25
|
+
*
|
|
26
|
+
* Only environments that resolve with the "workerd" condition are touched, so
|
|
27
|
+
* the browser bundle and a Node-run server keep the real modules.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// Vite ids use forward slashes on every platform, so these must too.
|
|
31
|
+
const compatFile = (name: string) =>
|
|
32
|
+
normalizePath(fileURLToPath(new URL(`../compat/${name}`, import.meta.url)));
|
|
33
|
+
|
|
34
|
+
export const WORKERD_WS_ADAPTER = compatFile("workerd-ws.cjs");
|
|
35
|
+
export const WORKERD_ENTER_WITH_PATCH = compatFile("workerd-enter-with.mjs");
|
|
36
|
+
|
|
37
|
+
const isWorkerd = (conditions: readonly string[] | undefined) =>
|
|
38
|
+
!!conditions?.includes("workerd");
|
|
39
|
+
|
|
40
|
+
// `ws` exports only its root, so a deep import never resolves to begin with.
|
|
41
|
+
const redirect = (id: string) => (id === "ws" ? WORKERD_WS_ADAPTER : null);
|
|
42
|
+
|
|
43
|
+
// JSON, CSS and the like reach a "pre" transform raw. Virtual ids often have no
|
|
44
|
+
// extension at all, and are JavaScript.
|
|
45
|
+
const NON_SCRIPT_FILE = /\.(?![cm]?[jt]sx?$)[a-z0-9]+$/i;
|
|
46
|
+
const NODE_MODULE = new Set(["module", "node:module"]);
|
|
47
|
+
const CREATE_REQUIRE_IMPL = "__b44CreateRequireImpl";
|
|
48
|
+
const CREATE_REQUIRE = "__b44CreateRequire";
|
|
49
|
+
|
|
50
|
+
// @babel/traverse is CommonJS: Node's ESM interop nests the function under
|
|
51
|
+
// `default`, while bundlers and Vitest hand it over directly.
|
|
52
|
+
const traverseAst = ((traverse as unknown as { default?: typeof traverse }).default ??
|
|
53
|
+
traverse) as unknown as typeof traverse.default;
|
|
54
|
+
|
|
55
|
+
const pathOf = (id: string) => id.split("?")[0] ?? id;
|
|
56
|
+
|
|
57
|
+
function parserPlugins(file: string): ParserPlugin[][] {
|
|
58
|
+
if (/\.tsx$/.test(file)) return [["typescript", "jsx"]];
|
|
59
|
+
if (/\.[cm]?ts$/.test(file)) return [["typescript"]];
|
|
60
|
+
// Apps routinely put JSX in .js, but JSX parsing can misread plain JS, so it
|
|
61
|
+
// is only the fallback.
|
|
62
|
+
return [[], ["jsx"]];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseModule(code: string, file: string): t.File | null {
|
|
66
|
+
for (const plugins of parserPlugins(file)) {
|
|
67
|
+
try {
|
|
68
|
+
return parse(code, {
|
|
69
|
+
// A module is ESM only if it has ESM syntax: text that merely looks
|
|
70
|
+
// like an import, inside a string or comment, does not count.
|
|
71
|
+
sourceType: /\.c[jt]s$/.test(file) ? "script" : /\.m[jt]s$/.test(file) ? "module" : "unambiguous",
|
|
72
|
+
allowReturnOutsideFunction: true,
|
|
73
|
+
allowAwaitOutsideFunction: true,
|
|
74
|
+
plugins,
|
|
75
|
+
});
|
|
76
|
+
} catch {
|
|
77
|
+
// Try the next plugin set.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const isRequireOfNodeModule = (node: t.Node | null | undefined): boolean =>
|
|
84
|
+
node?.type === "CallExpression" &&
|
|
85
|
+
node.callee.type === "Identifier" &&
|
|
86
|
+
node.callee.name === "require" &&
|
|
87
|
+
node.arguments[0]?.type === "StringLiteral" &&
|
|
88
|
+
NODE_MODULE.has(node.arguments[0].value);
|
|
89
|
+
|
|
90
|
+
const keyName = (key: t.Node): string | undefined =>
|
|
91
|
+
key.type === "Identifier" ? key.name : key.type === "StringLiteral" ? key.value : undefined;
|
|
92
|
+
|
|
93
|
+
/** Whether `name` at `path` is node:module's createRequire, under any alias. */
|
|
94
|
+
function bindsCreateRequire(path: NodePath, name: string): boolean {
|
|
95
|
+
const binding = path.scope.getBinding(name);
|
|
96
|
+
if (!binding) return false;
|
|
97
|
+
const decl = binding.path.node;
|
|
98
|
+
const parent = binding.path.parent;
|
|
99
|
+
if (decl.type === "ImportSpecifier") {
|
|
100
|
+
return (
|
|
101
|
+
parent.type === "ImportDeclaration" &&
|
|
102
|
+
NODE_MODULE.has(parent.source.value) &&
|
|
103
|
+
keyName(decl.imported) === "createRequire"
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
// const { createRequire } = require("node:module")
|
|
107
|
+
if (decl.type === "VariableDeclarator" && decl.id.type === "ObjectPattern" && isRequireOfNodeModule(decl.init)) {
|
|
108
|
+
return decl.id.properties.some(
|
|
109
|
+
(prop) =>
|
|
110
|
+
prop.type === "ObjectProperty" &&
|
|
111
|
+
keyName(prop.key) === "createRequire" &&
|
|
112
|
+
prop.value.type === "Identifier" &&
|
|
113
|
+
prop.value.name === name,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Whether `name` at `path` is node:module itself, as a default, namespace or required binding. */
|
|
120
|
+
function bindsNodeModule(path: NodePath, name: string): boolean {
|
|
121
|
+
const binding = path.scope.getBinding(name);
|
|
122
|
+
if (!binding) return false;
|
|
123
|
+
const decl = binding.path.node;
|
|
124
|
+
const parent = binding.path.parent;
|
|
125
|
+
if (decl.type === "ImportDefaultSpecifier" || decl.type === "ImportNamespaceSpecifier") {
|
|
126
|
+
return parent.type === "ImportDeclaration" && NODE_MODULE.has(parent.source.value);
|
|
127
|
+
}
|
|
128
|
+
return decl.type === "VariableDeclarator" && decl.id.type === "Identifier" && isRequireOfNodeModule(decl.init);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The callee of a call that reaches node:module's createRequire, or null. */
|
|
132
|
+
function createRequireCallee(path: NodePath<t.CallExpression | t.OptionalCallExpression>): t.Node | null {
|
|
133
|
+
const callee = path.node.callee;
|
|
134
|
+
if (callee.type === "Identifier") {
|
|
135
|
+
return bindsCreateRequire(path, callee.name) ? callee : null;
|
|
136
|
+
}
|
|
137
|
+
if (callee.type !== "MemberExpression" || callee.computed || keyName(callee.property) !== "createRequire") {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
const object = callee.object;
|
|
141
|
+
if (object.type === "Identifier") return bindsNodeModule(path, object.name) ? callee : null;
|
|
142
|
+
return isRequireOfNodeModule(object) ? callee : null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface PatchOptions {
|
|
146
|
+
/** Also route `createRequire` calls through the query-dropping wrapper. */
|
|
147
|
+
createRequire: boolean;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Patches a workerd server module: imports the enterWith no-op ahead of a module
|
|
152
|
+
* that calls `.enterWith()`, and, with `createRequire`, routes node:module's
|
|
153
|
+
* `createRequire` through a wrapper that drops the id's query. Code is inserted
|
|
154
|
+
* on the first line after any shebang or directive prologue, so strict mode
|
|
155
|
+
* holds and line numbers stay put. Returns null when nothing applies.
|
|
156
|
+
*/
|
|
157
|
+
export function patchServerModule(code: string, id: string, options: PatchOptions): string | null {
|
|
158
|
+
const file = pathOf(id);
|
|
159
|
+
if (NON_SCRIPT_FILE.test(file.slice(file.lastIndexOf("/") + 1)) || file === WORKERD_ENTER_WITH_PATCH) return null;
|
|
160
|
+
// The AST makes the call; this only skips files that cannot contain one,
|
|
161
|
+
// including calls a formatter split across lines.
|
|
162
|
+
const mayEnterWith = code.includes("enterWith");
|
|
163
|
+
const mayCreateRequire =
|
|
164
|
+
options.createRequire && code.includes("createRequire") && !code.includes(CREATE_REQUIRE_IMPL);
|
|
165
|
+
if (!mayEnterWith && !mayCreateRequire) return null;
|
|
166
|
+
|
|
167
|
+
const ast = parseModule(code, file);
|
|
168
|
+
if (!ast) return null;
|
|
169
|
+
|
|
170
|
+
let callsEnterWith = false;
|
|
171
|
+
const callees: t.Node[] = [];
|
|
172
|
+
const visit = (path: NodePath<t.CallExpression | t.OptionalCallExpression>) => {
|
|
173
|
+
const callee = path.node.callee;
|
|
174
|
+
if (
|
|
175
|
+
mayEnterWith &&
|
|
176
|
+
(callee.type === "MemberExpression" || callee.type === "OptionalMemberExpression") &&
|
|
177
|
+
!callee.computed &&
|
|
178
|
+
keyName(callee.property) === "enterWith"
|
|
179
|
+
) {
|
|
180
|
+
callsEnterWith = true;
|
|
181
|
+
}
|
|
182
|
+
const target = mayCreateRequire ? createRequireCallee(path) : null;
|
|
183
|
+
if (target) callees.push(target);
|
|
184
|
+
};
|
|
185
|
+
traverseAst(ast, { CallExpression: visit, OptionalCallExpression: visit });
|
|
186
|
+
if (!callsEnterWith && callees.length === 0) return null;
|
|
187
|
+
|
|
188
|
+
const isEsm = ast.program.sourceType === "module";
|
|
189
|
+
let prefix = "";
|
|
190
|
+
if (callsEnterWith) {
|
|
191
|
+
const patch = JSON.stringify(WORKERD_ENTER_WITH_PATCH);
|
|
192
|
+
// An ESM import in a CommonJS module would turn it into ESM; require it there.
|
|
193
|
+
prefix += isEsm ? `import ${patch};` : `require(${patch});`;
|
|
194
|
+
}
|
|
195
|
+
if (callees.length > 0) {
|
|
196
|
+
prefix += isEsm
|
|
197
|
+
? `import { createRequire as ${CREATE_REQUIRE_IMPL} } from "node:module";`
|
|
198
|
+
: `const { createRequire: ${CREATE_REQUIRE_IMPL} } = require("node:module");`;
|
|
199
|
+
prefix += `const ${CREATE_REQUIRE} = (path) => ${CREATE_REQUIRE_IMPL}(String(path).replace(/[?#].*$/, ""));`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// A shebang line cannot be followed on the same line, so the code goes at the
|
|
203
|
+
// start of the next one.
|
|
204
|
+
const { interpreter, directives } = ast.program;
|
|
205
|
+
let afterInterpreter = 0;
|
|
206
|
+
if (interpreter) {
|
|
207
|
+
const newline = code.indexOf("\n", interpreter.end!);
|
|
208
|
+
if (newline === -1) prefix = `\n${prefix}`;
|
|
209
|
+
afterInterpreter = newline === -1 ? code.length : newline + 1;
|
|
210
|
+
}
|
|
211
|
+
const insertAt = Math.max(afterInterpreter, ...directives.map((d) => d.end!));
|
|
212
|
+
// A directive may end without a semicolon, and code cannot follow it directly.
|
|
213
|
+
if (insertAt > afterInterpreter && code[insertAt - 1] !== ";") prefix = `;${prefix}`;
|
|
214
|
+
|
|
215
|
+
const edits = [
|
|
216
|
+
{ start: insertAt, end: insertAt, text: prefix },
|
|
217
|
+
...callees.map((node) => ({ start: node.start!, end: node.end!, text: CREATE_REQUIRE })),
|
|
218
|
+
].sort((a, b) => b.start - a.start);
|
|
219
|
+
let out = code;
|
|
220
|
+
for (const edit of edits) out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const devTransform = (code: string, id: string) => patchServerModule(code, id, { createRequire: true });
|
|
225
|
+
|
|
226
|
+
// Every edit keeps lines in place, so the input's source map still holds.
|
|
227
|
+
const transformResult = (code: string | null) => (code === null ? null : { code, map: null });
|
|
228
|
+
|
|
229
|
+
// Structural esbuild types: esbuild is Vite's dependency, not ours.
|
|
230
|
+
interface EsbuildBuild {
|
|
231
|
+
initialOptions: { conditions?: string[] };
|
|
232
|
+
onResolve(
|
|
233
|
+
options: { filter: RegExp },
|
|
234
|
+
callback: (args: { path: string }) => { path: string } | undefined,
|
|
235
|
+
): void;
|
|
236
|
+
onLoad(
|
|
237
|
+
options: { filter: RegExp; namespace: string },
|
|
238
|
+
callback: (args: { path: string }) => Promise<{ contents: string; loader: "js" } | undefined>,
|
|
239
|
+
): void;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Dev pre-bundles dependencies without running Vite plugins, so the dependency
|
|
243
|
+
// optimizer, which only runs in dev, needs its own copy. Each copy checks the
|
|
244
|
+
// build's conditions itself: Cloudflare's plugin sets them after ours has run.
|
|
245
|
+
function esbuildOptimizerPlugin() {
|
|
246
|
+
return {
|
|
247
|
+
name: "base44-workerd-compat",
|
|
248
|
+
setup(build: EsbuildBuild) {
|
|
249
|
+
if (!isWorkerd(build.initialOptions.conditions)) return;
|
|
250
|
+
build.onResolve({ filter: /^ws$/ }, () => ({ path: WORKERD_WS_ADAPTER }));
|
|
251
|
+
// Other namespaces hold virtual modules with no file to read.
|
|
252
|
+
build.onLoad({ filter: /\.[cm]?js$/, namespace: "file" }, async (args) => {
|
|
253
|
+
const code = devTransform(await readFile(args.path, "utf8"), args.path);
|
|
254
|
+
return code === null ? undefined : { contents: code, loader: "js" };
|
|
255
|
+
});
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function rolldownOptimizerPlugin() {
|
|
261
|
+
let active = false;
|
|
262
|
+
return {
|
|
263
|
+
name: "base44-workerd-compat",
|
|
264
|
+
options(options: { resolve?: { conditionNames?: string[] } }) {
|
|
265
|
+
active = isWorkerd(options.resolve?.conditionNames);
|
|
266
|
+
},
|
|
267
|
+
resolveId: (id: string) => (active ? redirect(id) : null),
|
|
268
|
+
transform: (code: string, id: string) => (active ? transformResult(devTransform(code, id)) : null),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const OPTIMIZED_DEP = /[\\/]node_modules[\\/]\.vite[\\/]/;
|
|
273
|
+
|
|
274
|
+
export interface WorkerdCompatOptions {
|
|
275
|
+
/** Which dependency optimizer to hook. Defaults to the installed Vite's. */
|
|
276
|
+
rolldown?: boolean;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function workerdCompatPlugin({ rolldown = isVite8OrLater }: WorkerdCompatOptions = {}): Plugin {
|
|
280
|
+
return {
|
|
281
|
+
name: "base44-workerd-compat",
|
|
282
|
+
enforce: "pre",
|
|
283
|
+
applyToEnvironment: (environment) =>
|
|
284
|
+
isWorkerd(environment.config.resolve.conditions),
|
|
285
|
+
configEnvironment(name) {
|
|
286
|
+
if (name === "client") return;
|
|
287
|
+
const optimizeDeps = rolldown
|
|
288
|
+
? { rolldownOptions: { plugins: [rolldownOptimizerPlugin()] } }
|
|
289
|
+
: { esbuildOptions: { plugins: [esbuildOptimizerPlugin()] } };
|
|
290
|
+
// Vite 7's types do not know `rolldownOptions`, hence the cast.
|
|
291
|
+
return { optimizeDeps } as EnvironmentOptions;
|
|
292
|
+
},
|
|
293
|
+
resolveId: (id) => redirect(id),
|
|
294
|
+
transform(code, id) {
|
|
295
|
+
// The optimizer already patched its output.
|
|
296
|
+
if (OPTIMIZED_DEP.test(id)) return null;
|
|
297
|
+
return transformResult(patchServerModule(code, id, { createRequire: this.environment.mode === "dev" }));
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}
|