rwsdk 0.1.0-alpha.9 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runtime/client.d.ts +3 -1
- package/dist/runtime/client.js +15 -11
- package/dist/runtime/clientNavigation.d.ts +3 -0
- package/dist/runtime/clientNavigation.js +43 -0
- package/dist/runtime/entries/client.d.ts +1 -0
- package/dist/runtime/entries/client.js +1 -0
- package/dist/runtime/entries/worker.d.ts +2 -0
- package/dist/runtime/entries/worker.js +2 -0
- package/dist/runtime/imports/ClientOnly.d.ts +3 -0
- package/dist/runtime/imports/ClientOnly.js +8 -0
- package/dist/runtime/imports/NoSSRStub.d.ts +1 -0
- package/dist/runtime/imports/NoSSRStub.js +4 -0
- package/dist/runtime/imports/client.js +15 -2
- package/dist/runtime/imports/worker.d.ts +1 -1
- package/dist/runtime/imports/worker.js +7 -5
- package/dist/runtime/lib/db/DOWorkerDialect.d.ts +29 -0
- package/dist/runtime/lib/db/DOWorkerDialect.js +66 -0
- package/dist/runtime/lib/db/SqliteDurableObject.d.ts +14 -0
- package/dist/runtime/lib/db/SqliteDurableObject.js +42 -0
- package/dist/runtime/lib/db/create.d.ts +3 -0
- package/dist/runtime/lib/db/create.js +36 -0
- package/dist/runtime/lib/db/createDb.d.ts +2 -0
- package/dist/runtime/lib/db/createDb.js +33 -0
- package/dist/runtime/lib/db/index.d.ts +3 -0
- package/dist/runtime/lib/db/index.js +3 -0
- package/dist/runtime/lib/db/logger.d.ts +2 -0
- package/dist/runtime/lib/db/logger.js +41 -0
- package/dist/runtime/lib/db/migrations.d.ts +23 -0
- package/dist/runtime/lib/db/migrations.js +34 -0
- package/dist/runtime/lib/db/types.d.ts +0 -0
- package/dist/runtime/lib/db/types.js +1 -0
- package/dist/runtime/lib/debug.d.ts +2 -0
- package/dist/runtime/lib/debug.js +36 -0
- package/dist/runtime/lib/router.d.ts +6 -1
- package/dist/runtime/lib/router.js +9 -2
- package/dist/runtime/register/ssr.d.ts +2 -0
- package/dist/runtime/register/ssr.js +14 -1
- package/dist/runtime/register/worker.d.ts +1 -1
- package/dist/runtime/register/worker.js +5 -2
- package/dist/runtime/render/renderRscThenableToHtmlStream.d.ts +2 -1
- package/dist/runtime/render/renderRscThenableToHtmlStream.js +17 -3
- package/dist/runtime/render/renderToStream.d.ts +9 -0
- package/dist/runtime/render/renderToStream.js +26 -0
- package/dist/runtime/render/renderToString.d.ts +7 -0
- package/dist/runtime/render/renderToString.js +26 -0
- package/dist/runtime/render/transformRscToHtmlStream.js +1 -0
- package/dist/runtime/worker.js +17 -10
- package/dist/scripts/debug-sync.mjs +8 -6
- package/dist/scripts/worker-run.mjs +1 -0
- package/dist/vite/configPlugin.d.mts +2 -2
- package/dist/vite/configPlugin.mjs +10 -21
- package/dist/vite/createDirectiveLookupPlugin.d.mts +1 -0
- package/dist/vite/createDirectiveLookupPlugin.mjs +88 -57
- package/dist/vite/devServerTimingPlugin.d.mts +2 -0
- package/dist/vite/devServerTimingPlugin.mjs +24 -0
- package/dist/vite/directivesPlugin.mjs +168 -70
- package/dist/vite/findImportSpecifiers.d.mts +16 -0
- package/dist/vite/findImportSpecifiers.mjs +152 -0
- package/dist/vite/findImportSpecifiers.test.d.mts +1 -0
- package/dist/vite/findImportSpecifiers.test.mjs +73 -0
- package/dist/vite/findSpecifiers.d.mts +31 -0
- package/dist/vite/findSpecifiers.mjs +230 -0
- package/dist/vite/hasDirective.d.mts +7 -0
- package/dist/vite/hasDirective.mjs +54 -0
- package/dist/vite/hasOwnCloudflareVitePlugin.d.mts +3 -0
- package/dist/vite/hasOwnCloudflareVitePlugin.mjs +14 -0
- package/dist/vite/injectVitePreamblePlugin.d.mts +2 -2
- package/dist/vite/injectVitePreamblePlugin.mjs +5 -2
- package/dist/vite/invalidateModule.d.mts +2 -0
- package/dist/vite/invalidateModule.mjs +14 -0
- package/dist/vite/miniflareHMRPlugin.d.mts +8 -0
- package/dist/vite/miniflareHMRPlugin.mjs +133 -0
- package/dist/vite/normalizeModulePath.mjs +12 -1
- package/dist/vite/reactConditionsResolverPlugin.d.mts +1 -1
- package/dist/vite/reactConditionsResolverPlugin.mjs +64 -59
- package/dist/vite/redwoodPlugin.d.mts +2 -1
- package/dist/vite/redwoodPlugin.mjs +24 -7
- package/dist/vite/resolveModuleId.d.mts +6 -0
- package/dist/vite/resolveModuleId.mjs +14 -0
- package/dist/vite/ssrBridgePlugin.d.mts +5 -1
- package/dist/vite/ssrBridgePlugin.mjs +4 -43
- package/dist/vite/transformClientComponents.d.mts +1 -0
- package/dist/vite/transformClientComponents.mjs +61 -125
- package/dist/vite/transformJsxScriptTagsPlugin.mjs +14 -3
- package/dist/vite/transformServerFunctions.d.mts +11 -3
- package/dist/vite/transformServerFunctions.mjs +256 -171
- package/dist/vite/transformServerFunctions.test.mjs +22 -3
- package/dist/vite/useClientLookupPlugin.mjs +1 -0
- package/dist/vite/useServerLookupPlugin.mjs +1 -0
- package/dist/vite/useServerPlugin.d.mts +1 -1
- package/dist/vite/useServerPlugin.mjs +1 -1
- package/package.json +14 -3
|
@@ -186,11 +186,18 @@ export function render(Document, routes,
|
|
|
186
186
|
/**
|
|
187
187
|
* @param options - Configuration options for rendering.
|
|
188
188
|
* @param options.rscPayload - Toggle the RSC payload that's appended to the Document. Disabling this will mean that interactivity no longer works.
|
|
189
|
+
* @param options.ssr - Disable sever side rendering for all these routes. This only allow client side rendering`, which requires `rscPayload` to be enabled.
|
|
189
190
|
*/
|
|
190
|
-
options = {
|
|
191
|
+
options = {}) {
|
|
192
|
+
options = {
|
|
193
|
+
rscPayload: true,
|
|
194
|
+
ssr: true,
|
|
195
|
+
...options,
|
|
196
|
+
};
|
|
191
197
|
const documentMiddleware = ({ rw }) => {
|
|
192
198
|
rw.Document = Document;
|
|
193
|
-
rw.rscPayload = options.rscPayload;
|
|
199
|
+
rw.rscPayload = options.rscPayload ?? true;
|
|
200
|
+
rw.ssr = options.ssr ?? true;
|
|
194
201
|
};
|
|
195
202
|
return [documentMiddleware, ...routes];
|
|
196
203
|
}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
import memoize from "lodash/memoize";
|
|
1
2
|
import { createServerReference as baseCreateServerReference } from "react-server-dom-webpack/client.edge";
|
|
2
|
-
|
|
3
|
+
export const loadServerModule = memoize(async (id) => {
|
|
4
|
+
const { useServerLookup } = await import("virtual:use-server-lookup");
|
|
5
|
+
const moduleFn = useServerLookup[id];
|
|
6
|
+
if (!moduleFn) {
|
|
7
|
+
throw new Error(`(worker) No module found for '${id}' in module lookup for "use server" directive`);
|
|
8
|
+
}
|
|
9
|
+
return await moduleFn();
|
|
10
|
+
});
|
|
11
|
+
export const getServerModuleExport = async (id) => {
|
|
12
|
+
const [file, name] = id.split("#");
|
|
13
|
+
const module = await loadServerModule(file);
|
|
14
|
+
return module[name];
|
|
15
|
+
};
|
|
3
16
|
const ssrCallServer = async (id, args) => {
|
|
4
17
|
const action = await getServerModuleExport(id);
|
|
5
18
|
if (typeof action !== "function") {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare function registerServerReference(action: Function, id: string, name: string): Function;
|
|
2
|
-
export declare function registerClientReference<Target extends Record<string, any>>(id: string, exportName: string
|
|
2
|
+
export declare function registerClientReference<Target extends Record<string, any>>(id: string, exportName: string, value: any): any;
|
|
3
3
|
export declare function __smokeTestActionHandler(timestamp?: number): Promise<unknown>;
|
|
4
4
|
export declare function rscActionHandler(req: Request): Promise<unknown>;
|
|
@@ -8,9 +8,12 @@ export function registerServerReference(action, id, name) {
|
|
|
8
8
|
// Note: We no longer need to register in a Map since we use virtual lookup
|
|
9
9
|
return baseRegisterServerReference(action, id, name);
|
|
10
10
|
}
|
|
11
|
-
export function registerClientReference(id, exportName) {
|
|
11
|
+
export function registerClientReference(id, exportName, value) {
|
|
12
|
+
const wrappedValue = (value && typeof value === "function") || typeof value === "object"
|
|
13
|
+
? value
|
|
14
|
+
: () => null;
|
|
12
15
|
const reference = baseRegisterClientReference({}, id, exportName);
|
|
13
|
-
return Object.defineProperties(
|
|
16
|
+
return Object.defineProperties(wrappedValue, {
|
|
14
17
|
...Object.getOwnPropertyDescriptors(reference),
|
|
15
18
|
$$async: { value: true },
|
|
16
19
|
$$isClientReference: { value: true },
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type DocumentProps } from "../lib/router";
|
|
2
2
|
import { type RequestInfo } from "../requestInfo/types";
|
|
3
|
-
export declare const renderRscThenableToHtmlStream: ({ thenable, Document, requestInfo, }: {
|
|
3
|
+
export declare const renderRscThenableToHtmlStream: ({ thenable, Document, requestInfo, shouldSSR, }: {
|
|
4
4
|
thenable: any;
|
|
5
5
|
Document: React.FC<DocumentProps>;
|
|
6
6
|
requestInfo: RequestInfo;
|
|
7
|
+
shouldSSR: boolean;
|
|
7
8
|
}) => Promise<import("react-dom/server").ReactDOMServerReadableStream>;
|
|
@@ -1,9 +1,23 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { use } from "react";
|
|
3
3
|
import { renderToReadableStream } from "react-dom/server.edge";
|
|
4
|
-
export const renderRscThenableToHtmlStream = async ({ thenable, Document, requestInfo, }) => {
|
|
4
|
+
export const renderRscThenableToHtmlStream = async ({ thenable, Document, requestInfo, shouldSSR, }) => {
|
|
5
5
|
const Component = () => {
|
|
6
|
-
|
|
6
|
+
const node = use(thenable).node;
|
|
7
|
+
// todo(justinvdm, 18 Jun 2025): We can build on this later to allow users
|
|
8
|
+
// surface context. e.g:
|
|
9
|
+
// * we assign `user: requestInfo.clientCtx` here
|
|
10
|
+
// * user populates requestInfo.clientCtx on worker side
|
|
11
|
+
// * user can import a read only `import { clientCtx } from "rwsdk/client"`
|
|
12
|
+
// on client side
|
|
13
|
+
const clientContext = {
|
|
14
|
+
rw: {
|
|
15
|
+
ssr: shouldSSR,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
return (_jsxs(Document, { ...requestInfo, children: [_jsx("script", { nonce: requestInfo.rw.nonce, dangerouslySetInnerHTML: {
|
|
19
|
+
__html: `globalThis.__RWSDK_CONTEXT = ${JSON.stringify(clientContext)}`,
|
|
20
|
+
} }), _jsx("div", { id: "hydrate-root", children: node })] }));
|
|
7
21
|
};
|
|
8
22
|
return await renderToReadableStream(_jsx(Component, {}), {
|
|
9
23
|
nonce: requestInfo.rw.nonce,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ReactElement, FC } from "react";
|
|
2
|
+
import { DocumentProps } from "../lib/router";
|
|
3
|
+
export interface RenderToStreamOptions {
|
|
4
|
+
Document?: FC<DocumentProps>;
|
|
5
|
+
injectRSCPayload?: boolean;
|
|
6
|
+
onError?: (error: unknown) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const IdentityDocument: FC<DocumentProps>;
|
|
9
|
+
export declare const renderToStream: (element: ReactElement, { Document, injectRSCPayload: shouldInjectRSCPayload, onError, }?: RenderToStreamOptions) => Promise<ReadableStream>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { renderToRscStream } from "./renderToRscStream";
|
|
3
|
+
import { transformRscToHtmlStream } from "./transformRscToHtmlStream";
|
|
4
|
+
import { requestInfo } from "../requestInfo/worker";
|
|
5
|
+
import { injectRSCPayload } from "rsc-html-stream/server";
|
|
6
|
+
export const IdentityDocument = ({ children }) => (_jsx(_Fragment, { children: children }));
|
|
7
|
+
export const renderToStream = async (element, { Document = IdentityDocument, injectRSCPayload: shouldInjectRSCPayload = false, onError, } = {}) => {
|
|
8
|
+
let rscStream = renderToRscStream({
|
|
9
|
+
node: element,
|
|
10
|
+
actionResult: null,
|
|
11
|
+
onError,
|
|
12
|
+
});
|
|
13
|
+
if (shouldInjectRSCPayload) {
|
|
14
|
+
const [rscPayloadStream1, rscPayloadStream2] = rscStream.tee();
|
|
15
|
+
rscStream = rscPayloadStream1;
|
|
16
|
+
rscStream = rscStream.pipeThrough(injectRSCPayload(rscPayloadStream2, {
|
|
17
|
+
nonce: requestInfo.rw.nonce,
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
const htmlStream = await transformRscToHtmlStream({
|
|
21
|
+
stream: rscStream,
|
|
22
|
+
Document,
|
|
23
|
+
requestInfo,
|
|
24
|
+
});
|
|
25
|
+
return htmlStream;
|
|
26
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { FC, ReactElement } from "react";
|
|
2
|
+
import { DocumentProps } from "../lib/router";
|
|
3
|
+
export interface RenderToStringOptions {
|
|
4
|
+
Document?: FC<DocumentProps>;
|
|
5
|
+
injectRSCPayload?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const renderToString: (element: ReactElement, options?: RenderToStringOptions) => Promise<string>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { renderToStream } from "./renderToStream";
|
|
2
|
+
export const renderToString = async (element, options) => {
|
|
3
|
+
const stream = await new Promise((resolve, reject) => renderToStream(element, {
|
|
4
|
+
...options,
|
|
5
|
+
onError: reject,
|
|
6
|
+
})
|
|
7
|
+
.then(resolve)
|
|
8
|
+
.catch(reject));
|
|
9
|
+
const reader = stream.getReader();
|
|
10
|
+
const decoder = new TextDecoder();
|
|
11
|
+
let result = "";
|
|
12
|
+
try {
|
|
13
|
+
while (true) {
|
|
14
|
+
const { done, value } = await reader.read();
|
|
15
|
+
if (done)
|
|
16
|
+
break;
|
|
17
|
+
result += decoder.decode(value, { stream: true });
|
|
18
|
+
}
|
|
19
|
+
// Flush any remaining bytes
|
|
20
|
+
result += decoder.decode();
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
reader.releaseLock();
|
|
25
|
+
}
|
|
26
|
+
};
|
package/dist/runtime/worker.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { transformRscToHtmlStream } from "./render/transformRscToHtmlStream";
|
|
3
3
|
import { renderToRscStream } from "./render/renderToRscStream";
|
|
4
|
-
import { ssrWebpackRequire } from "rwsdk/__ssr_bridge";
|
|
5
4
|
import { rscActionHandler } from "./register/worker";
|
|
6
5
|
import { injectRSCPayload } from "rsc-html-stream/server";
|
|
7
6
|
import { ErrorResponse } from "./error";
|
|
@@ -9,6 +8,7 @@ import { getRequestInfo, runWithRequestInfo, runWithRequestInfoOverrides, } from
|
|
|
9
8
|
import { defineRoutes } from "./lib/router";
|
|
10
9
|
import { generateNonce } from "./lib/utils";
|
|
11
10
|
import { IS_DEV } from "./constants";
|
|
11
|
+
import { ssrWebpackRequire } from "./imports/worker";
|
|
12
12
|
export const defineApp = (routes) => {
|
|
13
13
|
return {
|
|
14
14
|
fetch: async (request, env, cf) => {
|
|
@@ -39,6 +39,8 @@ export const defineApp = (routes) => {
|
|
|
39
39
|
Document: DefaultDocument,
|
|
40
40
|
nonce: generateNonce(),
|
|
41
41
|
rscPayload: true,
|
|
42
|
+
ssr: true,
|
|
43
|
+
databases: new Map(),
|
|
42
44
|
};
|
|
43
45
|
const outerRequestInfo = {
|
|
44
46
|
request,
|
|
@@ -87,7 +89,8 @@ export const defineApp = (routes) => {
|
|
|
87
89
|
actionResult = await rscActionHandler(request);
|
|
88
90
|
}
|
|
89
91
|
const pageElement = createPageElement(requestInfo, Page);
|
|
90
|
-
const
|
|
92
|
+
const { rscPayload: shouldInjectRSCPayload } = rw;
|
|
93
|
+
let rscPayloadStream = renderToRscStream({
|
|
91
94
|
node: pageElement,
|
|
92
95
|
actionResult: actionResult instanceof Response ? null : actionResult,
|
|
93
96
|
onError,
|
|
@@ -99,17 +102,21 @@ export const defineApp = (routes) => {
|
|
|
99
102
|
},
|
|
100
103
|
});
|
|
101
104
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
let injectRSCPayloadStream;
|
|
106
|
+
if (shouldInjectRSCPayload) {
|
|
107
|
+
const [rscPayloadStream1, rscPayloadStream2] = rscPayloadStream.tee();
|
|
108
|
+
rscPayloadStream = rscPayloadStream1;
|
|
109
|
+
injectRSCPayloadStream = injectRSCPayload(rscPayloadStream2, {
|
|
110
|
+
nonce: rw.nonce,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
let html = await transformRscToHtmlStream({
|
|
114
|
+
stream: rscPayloadStream,
|
|
105
115
|
Document: rw.Document,
|
|
106
116
|
requestInfo: requestInfo,
|
|
107
117
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
html = htmlStream.pipeThrough(injectRSCPayload(rscPayloadStream2, {
|
|
111
|
-
nonce: rw.nonce,
|
|
112
|
-
}));
|
|
118
|
+
if (injectRSCPayloadStream) {
|
|
119
|
+
html = html.pipeThrough(injectRSCPayloadStream);
|
|
113
120
|
}
|
|
114
121
|
return new Response(html, {
|
|
115
122
|
headers: {
|
|
@@ -8,12 +8,14 @@ export const debugSync = async (opts) => {
|
|
|
8
8
|
const syncCommand = `echo 🏗️ rebuilding... && pnpm build && rm -rf ${targetDir}/node_modules/rwsdk/dist ${targetDir}/node_modules/rwsdk/package.json && echo 📁 syncing sdk from ${process.cwd()} to ${targetDir}/node_modules/rwsdk/... && cp -r package.json dist ${targetDir}/node_modules/rwsdk/ && echo ✅ done syncing`;
|
|
9
9
|
// Run initial sync
|
|
10
10
|
await $({ stdio: "inherit", shell: true }) `${syncCommand}`;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
if (!process.env.NO_CLEAN_VITE) {
|
|
12
|
+
console.log("🧹 Cleaning Vite cache...");
|
|
13
|
+
await $({
|
|
14
|
+
stdio: "inherit",
|
|
15
|
+
shell: true,
|
|
16
|
+
cwd: targetDir,
|
|
17
|
+
}) `rm -rf node_modules/.vite`;
|
|
18
|
+
}
|
|
17
19
|
// If dev flag is present, clean vite cache and start dev server
|
|
18
20
|
if (dev) {
|
|
19
21
|
console.log("🚀 Starting dev server...");
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Plugin } from "vite";
|
|
2
2
|
export declare const cloudflareBuiltInModules: string[];
|
|
3
3
|
export declare const externalModules: string[];
|
|
4
|
-
export declare const configPlugin: ({ mode, silent, projectRootDir,
|
|
4
|
+
export declare const configPlugin: ({ mode, silent, projectRootDir, clientEntryPathnames, workerEntryPathname, }: {
|
|
5
5
|
mode: "development" | "production";
|
|
6
6
|
silent: boolean;
|
|
7
7
|
projectRootDir: string;
|
|
8
|
-
|
|
8
|
+
clientEntryPathnames: string[];
|
|
9
9
|
workerEntryPathname: string;
|
|
10
10
|
}) => Plugin;
|
|
@@ -15,7 +15,7 @@ export const externalModules = [
|
|
|
15
15
|
...builtinModules,
|
|
16
16
|
...builtinModules.map((m) => `node:${m}`),
|
|
17
17
|
];
|
|
18
|
-
export const configPlugin = ({ mode, silent, projectRootDir,
|
|
18
|
+
export const configPlugin = ({ mode, silent, projectRootDir, clientEntryPathnames, workerEntryPathname, }) => ({
|
|
19
19
|
name: "rwsdk:config",
|
|
20
20
|
config: (_, { command }) => {
|
|
21
21
|
const baseConfig = {
|
|
@@ -39,9 +39,7 @@ export const configPlugin = ({ mode, silent, projectRootDir, clientEntryPathname
|
|
|
39
39
|
outDir: resolve(projectRootDir, "dist", "client"),
|
|
40
40
|
manifest: true,
|
|
41
41
|
rollupOptions: {
|
|
42
|
-
input:
|
|
43
|
-
client: clientEntryPathname,
|
|
44
|
-
},
|
|
42
|
+
input: clientEntryPathnames,
|
|
45
43
|
},
|
|
46
44
|
},
|
|
47
45
|
define: {
|
|
@@ -49,6 +47,7 @@ export const configPlugin = ({ mode, silent, projectRootDir, clientEntryPathname
|
|
|
49
47
|
},
|
|
50
48
|
optimizeDeps: {
|
|
51
49
|
noDiscovery: false,
|
|
50
|
+
include: ["rwsdk/client"],
|
|
52
51
|
esbuildOptions: {
|
|
53
52
|
jsx: "automatic",
|
|
54
53
|
jsxImportSource: "react",
|
|
@@ -58,23 +57,13 @@ export const configPlugin = ({ mode, silent, projectRootDir, clientEntryPathname
|
|
|
58
57
|
},
|
|
59
58
|
},
|
|
60
59
|
},
|
|
60
|
+
resolve: {
|
|
61
|
+
conditions: ["browser", "module"],
|
|
62
|
+
},
|
|
61
63
|
},
|
|
62
64
|
ssr: {
|
|
63
65
|
resolve: {
|
|
64
|
-
conditions: [
|
|
65
|
-
"workerd",
|
|
66
|
-
// context(justinvdm, 11 Jun 2025): Some packages meant for cloudflare workers, yet
|
|
67
|
-
// their deps have only node import conditions, e.g. `agents` package (meant for CF),
|
|
68
|
-
// has `pkce-challenge` package as a dep, which has only node import conditions.
|
|
69
|
-
// https://github.com/crouchcd/pkce-challenge/blob/master/package.json#L17
|
|
70
|
-
//
|
|
71
|
-
// Once the transformed code for this environment is in turn processed in the `worker` environment,
|
|
72
|
-
// @cloudflare/vite-plugin should take care of any relevant polyfills for deps with
|
|
73
|
-
// node builtins imports that can be polyfilled though, so it is worth us including this condition here.
|
|
74
|
-
// However, it does mean we will try to run packages meant for node that cannot be run on cloudflare workers.
|
|
75
|
-
// That's the trade-off, but arguably worth it. (context(justinvdm, 11 Jun 2025))
|
|
76
|
-
"node",
|
|
77
|
-
],
|
|
66
|
+
conditions: ["workerd", "module", "browser"],
|
|
78
67
|
noExternal: true,
|
|
79
68
|
},
|
|
80
69
|
define: {
|
|
@@ -84,11 +73,10 @@ export const configPlugin = ({ mode, silent, projectRootDir, clientEntryPathname
|
|
|
84
73
|
noDiscovery: false,
|
|
85
74
|
entries: [workerEntryPathname],
|
|
86
75
|
exclude: externalModules,
|
|
87
|
-
include: ["rwsdk/__ssr_bridge"],
|
|
76
|
+
include: ["rwsdk/__ssr", "rwsdk/__ssr_bridge"],
|
|
88
77
|
esbuildOptions: {
|
|
89
78
|
jsx: "automatic",
|
|
90
79
|
jsxImportSource: "react",
|
|
91
|
-
conditions: ["workerd"],
|
|
92
80
|
plugins: [],
|
|
93
81
|
},
|
|
94
82
|
},
|
|
@@ -108,6 +96,7 @@ export const configPlugin = ({ mode, silent, projectRootDir, clientEntryPathname
|
|
|
108
96
|
conditions: [
|
|
109
97
|
"workerd",
|
|
110
98
|
"react-server",
|
|
99
|
+
"module",
|
|
111
100
|
// context(justinvdm, 11 Jun 2025): Some packages meant for cloudflare workers, yet
|
|
112
101
|
// their deps have only node import conditions, e.g. `agents` package (meant for CF),
|
|
113
102
|
// has `pkce-challenge` package as a dep, which has only node import conditions.
|
|
@@ -126,7 +115,7 @@ export const configPlugin = ({ mode, silent, projectRootDir, clientEntryPathname
|
|
|
126
115
|
},
|
|
127
116
|
optimizeDeps: {
|
|
128
117
|
noDiscovery: false,
|
|
129
|
-
include: [],
|
|
118
|
+
include: ["rwsdk/worker"],
|
|
130
119
|
exclude: [],
|
|
131
120
|
entries: [workerEntryPathname],
|
|
132
121
|
esbuildOptions: {
|
|
@@ -1,26 +1,19 @@
|
|
|
1
1
|
import MagicString from "magic-string";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { readFile } from "fs/promises";
|
|
4
|
-
import { glob } from "glob";
|
|
5
4
|
import debug from "debug";
|
|
6
5
|
import { normalizeModulePath } from "./normalizeModulePath.mjs";
|
|
7
|
-
import { ensureAliasArray } from "./ensureAliasArray.mjs";
|
|
8
6
|
import { pathExists } from "fs-extra";
|
|
9
7
|
import { stat } from "fs/promises";
|
|
10
8
|
import { getSrcPaths } from "../lib/getSrcPaths.js";
|
|
9
|
+
import { hasDirective } from "./hasDirective.mjs";
|
|
11
10
|
export const findFilesContainingDirective = async ({ projectRootDir, files, directive, debugNamespace, }) => {
|
|
12
11
|
const log = debug(debugNamespace);
|
|
13
12
|
const verboseLog = debug(`verbose:${debugNamespace}`);
|
|
14
13
|
log("Starting search for '%s' files in projectRootDir=%s", directive, projectRootDir);
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
absolute: true,
|
|
19
|
-
nodir: true,
|
|
20
|
-
});
|
|
21
|
-
const allFiles = [...filesOutsideNodeModules, ...filesInsideNodeModules];
|
|
22
|
-
log("Found %d files to scan for '%s' directive", allFiles.length, directive);
|
|
23
|
-
for (const file of allFiles) {
|
|
14
|
+
const filesToScan = await getSrcPaths(projectRootDir);
|
|
15
|
+
log("Found %d files to scan for '%s' directive", filesToScan.length, directive);
|
|
16
|
+
for (const file of filesToScan) {
|
|
24
17
|
try {
|
|
25
18
|
const stats = await stat(file);
|
|
26
19
|
if (!stats.isFile()) {
|
|
@@ -29,18 +22,10 @@ export const findFilesContainingDirective = async ({ projectRootDir, files, dire
|
|
|
29
22
|
}
|
|
30
23
|
verboseLog("Scanning file: %s", file);
|
|
31
24
|
const content = await readFile(file, "utf-8");
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (trimmedLine.startsWith(`"${directive}"`) ||
|
|
37
|
-
trimmedLine.startsWith(`'${directive}'`)) {
|
|
38
|
-
const normalizedPath = normalizeModulePath(projectRootDir, file);
|
|
39
|
-
log("Found '%s' directive in file: %s -> %s", directive, file, normalizedPath);
|
|
40
|
-
files.add(normalizedPath);
|
|
41
|
-
}
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
25
|
+
if (hasDirective(content, directive)) {
|
|
26
|
+
const normalizedPath = normalizeModulePath(projectRootDir, file);
|
|
27
|
+
log("Found '%s' directive in file: %s -> %s", directive, file, normalizedPath);
|
|
28
|
+
files.add(normalizedPath);
|
|
44
29
|
}
|
|
45
30
|
}
|
|
46
31
|
catch (error) {
|
|
@@ -48,37 +33,79 @@ export const findFilesContainingDirective = async ({ projectRootDir, files, dire
|
|
|
48
33
|
}
|
|
49
34
|
}
|
|
50
35
|
log("Completed scan. Found %d %s files total", files.size, directive);
|
|
36
|
+
verboseLog("Found files for %s: %j", directive, Array.from(files));
|
|
51
37
|
};
|
|
52
|
-
const resolveOptimizedDep = async (projectRootDir,
|
|
38
|
+
const resolveOptimizedDep = async (projectRootDir, id, environment, debugNamespace) => {
|
|
53
39
|
const log = debug(debugNamespace);
|
|
54
40
|
const verboseLog = debug(`verbose:${debugNamespace}`);
|
|
55
41
|
try {
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const manifestPath =
|
|
60
|
-
|
|
42
|
+
const depsDir = environment === "client" ? "deps" : `deps_${environment}`;
|
|
43
|
+
const nodeModulesDepsDirPath = path.join("node_modules", ".vite", depsDir);
|
|
44
|
+
const depsDirPath = path.join(projectRootDir, nodeModulesDepsDirPath);
|
|
45
|
+
const manifestPath = path.join(depsDirPath, "_metadata.json");
|
|
46
|
+
log("Checking for manifest at: %s", manifestPath);
|
|
61
47
|
const manifestExists = await pathExists(manifestPath);
|
|
62
48
|
if (!manifestExists) {
|
|
63
|
-
|
|
49
|
+
log("Manifest not found at %s", manifestPath);
|
|
64
50
|
return undefined;
|
|
65
51
|
}
|
|
66
52
|
const manifestContent = await readFile(manifestPath, "utf-8");
|
|
67
53
|
const manifest = JSON.parse(manifestContent);
|
|
68
|
-
if (manifest.optimized && manifest.optimized[
|
|
69
|
-
const optimizedFile = manifest.optimized[
|
|
70
|
-
const optimizedPath =
|
|
71
|
-
log("Found optimized dependency:
|
|
54
|
+
if (manifest.optimized && manifest.optimized[id]) {
|
|
55
|
+
const optimizedFile = manifest.optimized[id].file;
|
|
56
|
+
const optimizedPath = path.join("/", nodeModulesDepsDirPath, optimizedFile);
|
|
57
|
+
log("Found optimized dependency: filePath=%s, optimizedPath=%s", id, optimizedPath);
|
|
72
58
|
return optimizedPath;
|
|
73
59
|
}
|
|
74
|
-
verboseLog("File
|
|
60
|
+
verboseLog("File not found in optimized dependencies: id=%s", id);
|
|
75
61
|
return undefined;
|
|
76
62
|
}
|
|
77
63
|
catch (error) {
|
|
78
|
-
verboseLog("Error resolving optimized dependency for
|
|
64
|
+
verboseLog("Error resolving optimized dependency for id=%s: %s", id, error);
|
|
79
65
|
return undefined;
|
|
80
66
|
}
|
|
81
67
|
};
|
|
68
|
+
const addOptimizedDepsEntries = async ({ projectRootDir, directive, environment, debugNamespace, files, }) => {
|
|
69
|
+
const log = debug(debugNamespace);
|
|
70
|
+
const verboseLog = debug(`verbose:${debugNamespace}`);
|
|
71
|
+
try {
|
|
72
|
+
const depsDir = environment === "client" ? "deps" : `deps_${environment}`;
|
|
73
|
+
const depsDirPath = path.join(projectRootDir, "node_modules", ".vite", depsDir);
|
|
74
|
+
const manifestPath = path.join(depsDirPath, "_metadata.json");
|
|
75
|
+
verboseLog("Checking for manifest at: %s", manifestPath);
|
|
76
|
+
const manifestExists = await pathExists(manifestPath);
|
|
77
|
+
if (!manifestExists) {
|
|
78
|
+
verboseLog("Manifest not found at %s", manifestPath);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const manifestContent = await readFile(manifestPath, "utf-8");
|
|
82
|
+
const manifest = JSON.parse(manifestContent);
|
|
83
|
+
for (const entryId of Object.keys(manifest.optimized)) {
|
|
84
|
+
if (entryId.startsWith("/node_modules/")) {
|
|
85
|
+
const srcPath = manifest.optimized[entryId].src;
|
|
86
|
+
const resolvedSrcPath = path.resolve(projectRootDir, "node_modules", ".vite", "deps", srcPath);
|
|
87
|
+
let contents;
|
|
88
|
+
try {
|
|
89
|
+
contents = await readFile(resolvedSrcPath, "utf-8");
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
verboseLog("Error reading file %s: %s", resolvedSrcPath, error);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (hasDirective(contents, directive)) {
|
|
96
|
+
log("Adding optimized entry to files: %s", entryId);
|
|
97
|
+
files.add(entryId);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
log("Skipping optimized entry %s because it does not contain the '%s' directive", entryId, directive);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
verboseLog("Error adding optimized deps entries: %s", error);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
82
109
|
export const createDirectiveLookupPlugin = async ({ projectRootDir, files, config, }) => {
|
|
83
110
|
const debugNamespace = `rwsdk:vite:${config.pluginName}`;
|
|
84
111
|
const log = debug(debugNamespace);
|
|
@@ -91,14 +118,26 @@ export const createDirectiveLookupPlugin = async ({ projectRootDir, files, confi
|
|
|
91
118
|
directive: config.directive,
|
|
92
119
|
debugNamespace,
|
|
93
120
|
});
|
|
121
|
+
let devServer;
|
|
94
122
|
return {
|
|
95
123
|
name: `rwsdk:${config.pluginName}`,
|
|
96
124
|
config(_, { command, isPreview }) {
|
|
97
125
|
isDev = !isPreview && command === "serve";
|
|
98
126
|
log("Development mode: %s", isDev);
|
|
99
127
|
},
|
|
100
|
-
|
|
128
|
+
configureServer(server) {
|
|
129
|
+
devServer = server;
|
|
130
|
+
},
|
|
131
|
+
async configEnvironment(env, viteConfig) {
|
|
101
132
|
log("Configuring environment: env=%s", env);
|
|
133
|
+
// Add optimized deps entries that match our pattern
|
|
134
|
+
await addOptimizedDepsEntries({
|
|
135
|
+
projectRootDir,
|
|
136
|
+
files,
|
|
137
|
+
directive: config.directive,
|
|
138
|
+
environment: env,
|
|
139
|
+
debugNamespace,
|
|
140
|
+
});
|
|
102
141
|
viteConfig.optimizeDeps ??= {};
|
|
103
142
|
viteConfig.optimizeDeps.esbuildOptions ??= {};
|
|
104
143
|
viteConfig.optimizeDeps.esbuildOptions.plugins ??= [];
|
|
@@ -125,26 +164,16 @@ export const createDirectiveLookupPlugin = async ({ projectRootDir, files, confi
|
|
|
125
164
|
if (shouldOptimizeForEnv) {
|
|
126
165
|
log("Applying optimizeDeps and aliasing for environment: %s", env);
|
|
127
166
|
viteConfig.optimizeDeps.include ??= [];
|
|
128
|
-
const aliases = ensureAliasArray(viteConfig);
|
|
129
167
|
for (const file of files) {
|
|
130
168
|
const actualFilePath = path.join(projectRootDir, file);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
viteConfig.optimizeDeps.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
verboseLog("Adding to optimizeDeps.entries: %s", actualFilePath);
|
|
140
|
-
const entries = Array.isArray(viteConfig.optimizeDeps.entries)
|
|
141
|
-
? viteConfig.optimizeDeps.entries
|
|
142
|
-
: [].concat(viteConfig.optimizeDeps.entries ?? []);
|
|
143
|
-
viteConfig.optimizeDeps.entries = entries;
|
|
144
|
-
entries.push(actualFilePath);
|
|
145
|
-
}
|
|
169
|
+
verboseLog("Adding to optimizeDeps.entries: %s", actualFilePath);
|
|
170
|
+
const entries = Array.isArray(viteConfig.optimizeDeps.entries)
|
|
171
|
+
? viteConfig.optimizeDeps.entries
|
|
172
|
+
: [].concat(viteConfig.optimizeDeps.entries ?? []);
|
|
173
|
+
viteConfig.optimizeDeps.entries = entries;
|
|
174
|
+
entries.push(actualFilePath);
|
|
146
175
|
}
|
|
147
|
-
log("Environment configuration complete for env=%s
|
|
176
|
+
log("Environment configuration complete for env=%s", env);
|
|
148
177
|
}
|
|
149
178
|
else {
|
|
150
179
|
log("Skipping optimizeDeps and aliasing for environment: %s", env);
|
|
@@ -155,18 +184,20 @@ export const createDirectiveLookupPlugin = async ({ projectRootDir, files, confi
|
|
|
155
184
|
if (source === config.virtualModuleName ||
|
|
156
185
|
source === `/@id/${config.virtualModuleName}`) {
|
|
157
186
|
log("Resolving %s module", config.virtualModuleName);
|
|
158
|
-
|
|
187
|
+
// context(justinvdm, 16 Jun 2025): Include .js extension
|
|
188
|
+
// so it goes through vite processing chain
|
|
189
|
+
return config.virtualModuleName + ".js";
|
|
159
190
|
}
|
|
160
191
|
verboseLog("No resolution for id=%s", source);
|
|
161
192
|
},
|
|
162
193
|
async load(id) {
|
|
163
194
|
verboseLog("Loading id=%s", id);
|
|
164
|
-
if (id === config.virtualModuleName) {
|
|
195
|
+
if (id === config.virtualModuleName + ".js") {
|
|
165
196
|
log("Loading %s module with %d files", config.virtualModuleName, files.size);
|
|
166
197
|
const environment = this.environment?.name || "client";
|
|
167
198
|
log("Current environment: %s, isDev: %s", environment, isDev);
|
|
168
199
|
const optimizedDeps = {};
|
|
169
|
-
if (isDev) {
|
|
200
|
+
if (isDev && devServer) {
|
|
170
201
|
for (const file of files) {
|
|
171
202
|
const resolvedPath = await resolveOptimizedDep(projectRootDir, file, environment, debugNamespace);
|
|
172
203
|
if (resolvedPath) {
|