rwsdk 1.0.0-beta.2-test.20250930092748 → 1.0.0-beta.20
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/lib/constants.d.mts +1 -0
- package/dist/lib/constants.mjs +1 -0
- package/dist/lib/e2e/constants.d.mts +14 -0
- package/dist/lib/e2e/constants.mjs +74 -0
- package/dist/lib/e2e/dev.mjs +0 -1
- package/dist/lib/e2e/environment.d.mts +1 -1
- package/dist/lib/e2e/environment.mjs +118 -18
- package/dist/lib/e2e/index.d.mts +1 -0
- package/dist/lib/e2e/index.mjs +1 -0
- package/dist/lib/e2e/poll.d.mts +1 -1
- package/dist/lib/e2e/testHarness.d.mts +36 -3
- package/dist/lib/e2e/testHarness.mjs +192 -116
- package/dist/runtime/client/client.d.ts +1 -0
- package/dist/runtime/client/client.js +2 -0
- package/dist/runtime/client/navigation.d.ts +8 -0
- package/dist/runtime/client/navigation.js +39 -31
- package/dist/runtime/entries/clientSSR.d.ts +1 -0
- package/dist/runtime/entries/clientSSR.js +3 -0
- package/dist/runtime/entries/router.d.ts +1 -0
- package/dist/runtime/entries/worker.d.ts +2 -0
- package/dist/runtime/entries/worker.js +2 -0
- package/dist/runtime/lib/db/createDb.d.ts +1 -2
- package/dist/runtime/lib/db/createDb.js +4 -0
- package/dist/runtime/lib/manifest.d.ts +1 -1
- package/dist/runtime/lib/manifest.js +7 -4
- package/dist/runtime/lib/realtime/client.js +8 -2
- package/dist/runtime/lib/router.d.ts +16 -21
- package/dist/runtime/lib/router.js +67 -1
- package/dist/runtime/lib/router.test.js +241 -0
- package/dist/runtime/lib/{rwContext.d.ts → types.d.ts} +1 -0
- package/dist/runtime/render/renderDocumentHtmlStream.d.ts +1 -1
- package/dist/runtime/render/renderToStream.d.ts +4 -2
- package/dist/runtime/render/renderToStream.js +21 -2
- package/dist/runtime/render/renderToString.d.ts +3 -1
- package/dist/runtime/requestInfo/types.d.ts +4 -1
- package/dist/runtime/requestInfo/utils.d.ts +9 -0
- package/dist/runtime/requestInfo/utils.js +44 -0
- package/dist/runtime/requestInfo/worker.js +3 -2
- package/dist/runtime/script.d.ts +1 -3
- package/dist/runtime/script.js +1 -10
- package/dist/runtime/state.d.ts +3 -0
- package/dist/runtime/state.js +13 -0
- package/dist/runtime/worker.js +25 -0
- package/dist/scripts/debug-sync.mjs +18 -20
- package/dist/scripts/worker-run.d.mts +1 -1
- package/dist/scripts/worker-run.mjs +50 -113
- package/dist/vite/buildApp.mjs +34 -2
- package/dist/vite/directiveModulesDevPlugin.mjs +1 -1
- package/dist/vite/envResolvers.d.mts +11 -0
- package/dist/vite/envResolvers.mjs +20 -0
- package/dist/vite/hmrStabilityPlugin.d.mts +2 -0
- package/dist/vite/hmrStabilityPlugin.mjs +68 -0
- package/dist/vite/knownDepsResolverPlugin.d.mts +0 -6
- package/dist/vite/knownDepsResolverPlugin.mjs +1 -12
- package/dist/vite/linkerPlugin.d.mts +2 -1
- package/dist/vite/linkerPlugin.mjs +11 -3
- package/dist/vite/linkerPlugin.test.mjs +15 -0
- package/dist/vite/redwoodPlugin.mjs +7 -9
- package/dist/vite/runDirectivesScan.mjs +57 -12
- package/dist/vite/ssrBridgePlugin.mjs +104 -34
- package/dist/vite/staleDepRetryPlugin.d.mts +2 -0
- package/dist/vite/staleDepRetryPlugin.mjs +69 -0
- package/dist/vite/statePlugin.d.mts +4 -0
- package/dist/vite/statePlugin.mjs +62 -0
- package/package.json +13 -7
- package/dist/vite/manifestPlugin.d.mts +0 -4
- package/dist/vite/manifestPlugin.mjs +0 -63
- /package/dist/runtime/lib/{rwContext.js → types.js} +0 -0
|
@@ -99,28 +99,20 @@ const findUp = async (names, startDir) => {
|
|
|
99
99
|
return undefined;
|
|
100
100
|
};
|
|
101
101
|
const getMonorepoRoot = async (startDir) => {
|
|
102
|
-
|
|
103
|
-
// `pnpm root` is the most reliable way to find the workspace root node_modules
|
|
104
|
-
const { stdout } = await $({
|
|
105
|
-
cwd: startDir,
|
|
106
|
-
}) `pnpm root`;
|
|
107
|
-
// pnpm root returns the node_modules path, so we go up one level
|
|
108
|
-
return path.resolve(stdout, "..");
|
|
109
|
-
}
|
|
110
|
-
catch (e) {
|
|
111
|
-
console.warn(`Could not determine pnpm root from ${startDir}. Falling back to file search.`);
|
|
112
|
-
const root = await findUp(["pnpm-workspace.yaml"], startDir);
|
|
113
|
-
if (root) {
|
|
114
|
-
return root;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
console.warn("Could not find pnpm monorepo root. Using parent directory of target as fallback.");
|
|
118
|
-
return path.resolve(startDir, "..");
|
|
102
|
+
return await findUp(["pnpm-workspace.yaml"], startDir);
|
|
119
103
|
};
|
|
120
104
|
const areDependenciesEqual = (deps1, deps2) => {
|
|
121
105
|
// Simple string comparison for this use case is sufficient
|
|
122
106
|
return JSON.stringify(deps1 ?? {}) === JSON.stringify(deps2 ?? {});
|
|
123
107
|
};
|
|
108
|
+
const isPlaygroundExample = async (targetDir, monorepoRoot) => {
|
|
109
|
+
const pkgJson = JSON.parse(await fs.readFile(path.join(monorepoRoot, "package.json"), "utf-8"));
|
|
110
|
+
if (pkgJson.name === "rw-sdk-monorepo") {
|
|
111
|
+
const playgroundDir = path.join(monorepoRoot, "playground");
|
|
112
|
+
return targetDir.startsWith(playgroundDir);
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
};
|
|
124
116
|
const performFullSync = async (sdkDir, targetDir, monorepoRoot) => {
|
|
125
117
|
console.log("📦 Performing full sync with tarball...");
|
|
126
118
|
let tarballPath = "";
|
|
@@ -178,8 +170,14 @@ const performSync = async (sdkDir, targetDir) => {
|
|
|
178
170
|
// Clean up vite cache in the target project
|
|
179
171
|
await cleanupViteEntries(targetDir);
|
|
180
172
|
const monorepoRoot = await getMonorepoRoot(targetDir);
|
|
173
|
+
const rootDir = monorepoRoot ?? targetDir;
|
|
181
174
|
const projectName = path.basename(targetDir);
|
|
182
|
-
|
|
175
|
+
if (monorepoRoot && (await isPlaygroundExample(targetDir, monorepoRoot))) {
|
|
176
|
+
console.log("Playground example detected. Skipping file sync; workspace linking will be used.");
|
|
177
|
+
console.log("✅ Done syncing");
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const installedSdkPackageJsonPath = path.join(rootDir, "node_modules", `.rwsync_${projectName}`, "node_modules", "rwsdk", "package.json");
|
|
183
181
|
let needsFullSync = false;
|
|
184
182
|
if (!existsSync(installedSdkPackageJsonPath)) {
|
|
185
183
|
console.log("No previous sync found, performing full sync.");
|
|
@@ -195,10 +193,10 @@ const performSync = async (sdkDir, targetDir) => {
|
|
|
195
193
|
}
|
|
196
194
|
}
|
|
197
195
|
if (needsFullSync) {
|
|
198
|
-
await performFullSync(sdkDir, targetDir,
|
|
196
|
+
await performFullSync(sdkDir, targetDir, rootDir);
|
|
199
197
|
}
|
|
200
198
|
else {
|
|
201
|
-
await performFastSync(sdkDir, targetDir,
|
|
199
|
+
await performFastSync(sdkDir, targetDir, rootDir);
|
|
202
200
|
}
|
|
203
201
|
console.log("✅ Done syncing");
|
|
204
202
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export {};
|
|
@@ -1,131 +1,68 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import { redwood } from "../vite/index.mjs";
|
|
11
|
-
const debug = baseDebug("rwsdk:worker-run");
|
|
12
|
-
export const runWorkerScript = async (relativeScriptPath) => {
|
|
1
|
+
import dbg from "debug";
|
|
2
|
+
import getPort from "get-port";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import * as vite from "vite";
|
|
5
|
+
import { createLogger } from "vite";
|
|
6
|
+
const debug = dbg("rwsdk:worker-run");
|
|
7
|
+
const main = async () => {
|
|
8
|
+
process.env.RWSDK_WORKER_RUN = "1";
|
|
9
|
+
const relativeScriptPath = process.argv[2];
|
|
13
10
|
if (!relativeScriptPath) {
|
|
14
11
|
console.error("Error: Script path is required");
|
|
15
12
|
console.log("\nUsage:");
|
|
16
|
-
console.log("
|
|
17
|
-
console.log("\nOptions:");
|
|
18
|
-
console.log(" RWSDK_WRANGLER_CONFIG Environment variable for config path");
|
|
13
|
+
console.log(" rwsdk worker-run <script-path>");
|
|
19
14
|
console.log("\nExamples:");
|
|
20
|
-
console.log("
|
|
21
|
-
console.log(" RWSDK_WRANGLER_CONFIG=custom.toml npm run worker:run src/scripts/seed.ts\n");
|
|
15
|
+
console.log(" rwsdk worker-run src/scripts/seed.ts\n");
|
|
22
16
|
process.exit(1);
|
|
23
17
|
}
|
|
24
|
-
const scriptPath = resolve(process.cwd(), relativeScriptPath);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const workerConfig = unstable_readConfig({
|
|
31
|
-
config: workerConfigPath,
|
|
32
|
-
env: "dev",
|
|
33
|
-
});
|
|
34
|
-
const durableObjectsToExport = workerConfig.durable_objects?.bindings
|
|
35
|
-
.filter((binding) => !binding.script_name)
|
|
36
|
-
.map((binding) => binding.class_name) ?? [];
|
|
37
|
-
const workerEntryRelativePath = workerConfig.main;
|
|
38
|
-
const workerEntryPath = workerEntryRelativePath ?? path.join(process.cwd(), "src/worker.tsx");
|
|
39
|
-
const durableObjectExports = [];
|
|
40
|
-
if (durableObjectsToExport.length > 0) {
|
|
41
|
-
const resolver = enhancedResolve.create.sync({
|
|
42
|
-
extensions: [".mts", ".ts", ".tsx", ".mjs", ".js", ".jsx", ".json"],
|
|
43
|
-
});
|
|
44
|
-
const workerEntryContents = await readFile(workerEntryPath, "utf-8");
|
|
45
|
-
const workerEntryAst = parse(Lang.Tsx, workerEntryContents);
|
|
46
|
-
const exportDeclarations = [
|
|
47
|
-
...workerEntryAst.root().findAll('export { $$$EXPORTS } from "$MODULE"'),
|
|
48
|
-
...workerEntryAst.root().findAll("export { $$$EXPORTS } from '$MODULE'"),
|
|
49
|
-
...workerEntryAst.root().findAll("export { $$$EXPORTS } from '$MODULE'"),
|
|
50
|
-
];
|
|
51
|
-
for (const exportDeclaration of exportDeclarations) {
|
|
52
|
-
const moduleMatch = exportDeclaration.getMatch("MODULE");
|
|
53
|
-
const exportsMatch = exportDeclaration.getMultipleMatches("EXPORTS");
|
|
54
|
-
if (!moduleMatch || exportsMatch.length === 0) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
const modulePath = moduleMatch.text();
|
|
58
|
-
const specifiers = exportsMatch.map((m) => m.text().trim());
|
|
59
|
-
for (const specifier of specifiers) {
|
|
60
|
-
if (durableObjectsToExport.includes(specifier)) {
|
|
61
|
-
const resolvedPath = resolver(path.dirname(workerEntryPath), modulePath);
|
|
62
|
-
durableObjectExports.push(`export { ${specifier} } from "${resolvedPath}";`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
18
|
+
const scriptPath = path.resolve(process.cwd(), relativeScriptPath);
|
|
19
|
+
const port = await getPort();
|
|
20
|
+
let server;
|
|
21
|
+
const cleanup = async () => {
|
|
22
|
+
if (server) {
|
|
23
|
+
await server.close();
|
|
65
24
|
}
|
|
66
|
-
|
|
67
|
-
const tmpDir = await tmp.dir({
|
|
68
|
-
prefix: "rw-worker-run-",
|
|
69
|
-
unsafeCleanup: true,
|
|
70
|
-
});
|
|
71
|
-
const relativeTmpWorkerEntryPath = "worker.tsx";
|
|
72
|
-
const tmpWorkerPath = path.join(tmpDir.path, "wrangler.json");
|
|
73
|
-
const tmpWorkerEntryPath = path.join(tmpDir.path, relativeTmpWorkerEntryPath);
|
|
74
|
-
const scriptWorkerConfig = {
|
|
75
|
-
...workerConfig,
|
|
76
|
-
configPath: tmpWorkerPath,
|
|
77
|
-
userConfigPath: tmpWorkerPath,
|
|
78
|
-
main: relativeTmpWorkerEntryPath,
|
|
25
|
+
process.exit();
|
|
79
26
|
};
|
|
27
|
+
process.on("SIGINT", cleanup);
|
|
28
|
+
process.on("SIGTERM", cleanup);
|
|
80
29
|
try {
|
|
81
|
-
await
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
configFile: false,
|
|
91
|
-
plugins: [
|
|
92
|
-
redwood({
|
|
93
|
-
configPath: tmpWorkerPath,
|
|
94
|
-
includeCloudflarePlugin: true,
|
|
95
|
-
entry: {
|
|
96
|
-
worker: tmpWorkerEntryPath,
|
|
97
|
-
},
|
|
98
|
-
}),
|
|
99
|
-
],
|
|
30
|
+
server = await vite.createServer({
|
|
31
|
+
logLevel: "silent",
|
|
32
|
+
build: {
|
|
33
|
+
outDir: ".rwsdk",
|
|
34
|
+
},
|
|
35
|
+
customLogger: createLogger("info", {
|
|
36
|
+
prefix: "[rwsdk]",
|
|
37
|
+
allowClearScreen: true,
|
|
38
|
+
}),
|
|
100
39
|
server: {
|
|
101
|
-
port
|
|
40
|
+
port,
|
|
41
|
+
host: "localhost",
|
|
102
42
|
},
|
|
103
43
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
44
|
+
await server.listen();
|
|
45
|
+
const url = `http://localhost:${port}/__worker-run?script=${scriptPath}`;
|
|
46
|
+
debug("Fetching %s", url);
|
|
47
|
+
const response = await fetch(url);
|
|
48
|
+
debug("Response from worker: %s", response);
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const errorText = await response.text();
|
|
51
|
+
console.error(`Error: worker-run script failed with status ${response.status}.`);
|
|
52
|
+
if (errorText) {
|
|
53
|
+
console.error("Response:", errorText);
|
|
111
54
|
}
|
|
112
|
-
|
|
113
|
-
await fetch(`http://localhost:${address.port}/`);
|
|
114
|
-
debug("Worker fetched successfully");
|
|
115
|
-
}
|
|
116
|
-
finally {
|
|
117
|
-
debug("Closing server...");
|
|
118
|
-
server.close();
|
|
119
|
-
debug("Server closed");
|
|
55
|
+
process.exit(1);
|
|
120
56
|
}
|
|
57
|
+
const responseText = await response.text();
|
|
58
|
+
debug("Response from worker: %s", responseText);
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
console.error("rwsdk: Error running script:\n\n%s", e.message);
|
|
62
|
+
process.exit(1);
|
|
121
63
|
}
|
|
122
64
|
finally {
|
|
123
|
-
|
|
124
|
-
debug("Temporary files cleaned up");
|
|
65
|
+
await cleanup();
|
|
125
66
|
}
|
|
126
|
-
// todo(justinvdm, 01 Apr 2025): Investigate what handles are remaining open
|
|
127
|
-
process.exit(0);
|
|
128
67
|
};
|
|
129
|
-
|
|
130
|
-
runWorkerScript(process.argv[2]);
|
|
131
|
-
}
|
|
68
|
+
main();
|
package/dist/vite/buildApp.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { INTERMEDIATES_OUTPUT_DIR } from "../lib/constants.mjs";
|
|
3
6
|
import { runDirectivesScan } from "./runDirectivesScan.mjs";
|
|
4
7
|
const log = debug("rwsdk:vite:build-app");
|
|
5
8
|
/**
|
|
@@ -10,7 +13,36 @@ const log = debug("rwsdk:vite:build-app");
|
|
|
10
13
|
* @see docs/architecture/productionBuildProcess.md
|
|
11
14
|
*/
|
|
12
15
|
export async function buildApp({ builder, clientEntryPoints, clientFiles, serverFiles, projectRootDir, workerEntryPathname, }) {
|
|
16
|
+
await rm(resolve(projectRootDir, "dist"), { recursive: true, force: true });
|
|
13
17
|
const workerEnv = builder.environments.worker;
|
|
18
|
+
// Run a pre-scan build pass to allow plugins to set up and generate code
|
|
19
|
+
// before scanning.
|
|
20
|
+
console.log("Running plugin setup pass...");
|
|
21
|
+
process.env.RWSDK_BUILD_PASS = "plugin-setup";
|
|
22
|
+
const tempEntryPath = resolve(INTERMEDIATES_OUTPUT_DIR, "temp-entry.js");
|
|
23
|
+
try {
|
|
24
|
+
if (!existsSync(dirname(tempEntryPath))) {
|
|
25
|
+
await mkdir(dirname(tempEntryPath), { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
await writeFile(tempEntryPath, "");
|
|
28
|
+
const originalWorkerBuildConfig = workerEnv.config.build;
|
|
29
|
+
workerEnv.config.build = {
|
|
30
|
+
...originalWorkerBuildConfig,
|
|
31
|
+
write: false,
|
|
32
|
+
rollupOptions: {
|
|
33
|
+
...originalWorkerBuildConfig?.rollupOptions,
|
|
34
|
+
input: {
|
|
35
|
+
index: tempEntryPath,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
await builder.build(workerEnv);
|
|
40
|
+
// Restore the original config
|
|
41
|
+
workerEnv.config.build = originalWorkerBuildConfig;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
await rm(tempEntryPath, { force: true });
|
|
45
|
+
}
|
|
14
46
|
await runDirectivesScan({
|
|
15
47
|
rootConfig: builder.config,
|
|
16
48
|
environments: builder.environments,
|
|
@@ -18,7 +50,7 @@ export async function buildApp({ builder, clientEntryPoints, clientFiles, server
|
|
|
18
50
|
serverFiles,
|
|
19
51
|
entries: [workerEntryPathname],
|
|
20
52
|
});
|
|
21
|
-
console.log("Building worker
|
|
53
|
+
console.log("Building worker...");
|
|
22
54
|
process.env.RWSDK_BUILD_PASS = "worker";
|
|
23
55
|
await builder.build(workerEnv);
|
|
24
56
|
log("Used client files after worker build & filtering: %O", Array.from(clientFiles));
|
|
@@ -38,7 +38,7 @@ export const directiveModulesDevPlugin = ({ clientFiles, serverFiles, projectRoo
|
|
|
38
38
|
return {
|
|
39
39
|
name: "rwsdk:directive-modules-dev",
|
|
40
40
|
configureServer(server) {
|
|
41
|
-
if (!process.env.VITE_IS_DEV_SERVER
|
|
41
|
+
if (!process.env.VITE_IS_DEV_SERVER) {
|
|
42
42
|
resolveScanPromise();
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import enhancedResolve from "enhanced-resolve";
|
|
2
|
+
export declare const ENV_RESOLVERS: {
|
|
3
|
+
ssr: enhancedResolve.ResolveFunction;
|
|
4
|
+
worker: enhancedResolve.ResolveFunction;
|
|
5
|
+
client: enhancedResolve.ResolveFunction;
|
|
6
|
+
};
|
|
7
|
+
export declare const maybeResolveEnvImport: ({ id, envName, projectRootDir, }: {
|
|
8
|
+
id: string;
|
|
9
|
+
envName: keyof typeof ENV_RESOLVERS;
|
|
10
|
+
projectRootDir: string;
|
|
11
|
+
}) => string | undefined;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import enhancedResolve from "enhanced-resolve";
|
|
2
|
+
export const ENV_RESOLVERS = {
|
|
3
|
+
ssr: enhancedResolve.create.sync({
|
|
4
|
+
conditionNames: ["workerd", "worker", "edge", "default"],
|
|
5
|
+
}),
|
|
6
|
+
worker: enhancedResolve.create.sync({
|
|
7
|
+
conditionNames: ["react-server", "workerd", "worker", "edge", "default"],
|
|
8
|
+
}),
|
|
9
|
+
client: enhancedResolve.create.sync({
|
|
10
|
+
conditionNames: ["browser", "default"],
|
|
11
|
+
}),
|
|
12
|
+
};
|
|
13
|
+
export const maybeResolveEnvImport = ({ id, envName, projectRootDir, }) => {
|
|
14
|
+
try {
|
|
15
|
+
return ENV_RESOLVERS[envName](projectRootDir, id) || undefined;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import debug from "debug";
|
|
2
|
+
const log = debug("rws-vite-plugin:hmr-stability");
|
|
3
|
+
let stabilityPromise = null;
|
|
4
|
+
let stabilityResolver = null;
|
|
5
|
+
let debounceTimer = null;
|
|
6
|
+
const DEBOUNCE_MS = 500;
|
|
7
|
+
function startWaitingForStability() {
|
|
8
|
+
if (!stabilityPromise) {
|
|
9
|
+
log("Starting to wait for server stability...");
|
|
10
|
+
stabilityPromise = new Promise((resolve) => {
|
|
11
|
+
stabilityResolver = resolve;
|
|
12
|
+
});
|
|
13
|
+
// Start the timer. If it fires, we're stable.
|
|
14
|
+
debounceTimer = setTimeout(finishWaiting, DEBOUNCE_MS);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function activityDetected() {
|
|
18
|
+
if (stabilityPromise) {
|
|
19
|
+
// If we're waiting for stability, reset the timer.
|
|
20
|
+
log("Activity detected, resetting stability timer.");
|
|
21
|
+
if (debounceTimer)
|
|
22
|
+
clearTimeout(debounceTimer);
|
|
23
|
+
debounceTimer = setTimeout(finishWaiting, DEBOUNCE_MS);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function finishWaiting() {
|
|
27
|
+
if (stabilityResolver) {
|
|
28
|
+
log("Server appears stable. Resolving promise.");
|
|
29
|
+
stabilityResolver();
|
|
30
|
+
}
|
|
31
|
+
stabilityPromise = null;
|
|
32
|
+
stabilityResolver = null;
|
|
33
|
+
debounceTimer = null;
|
|
34
|
+
}
|
|
35
|
+
export function hmrStabilityPlugin() {
|
|
36
|
+
return {
|
|
37
|
+
name: "rws-vite-plugin:hmr-stability",
|
|
38
|
+
// Monitor server activity
|
|
39
|
+
transform() {
|
|
40
|
+
activityDetected();
|
|
41
|
+
return null;
|
|
42
|
+
},
|
|
43
|
+
configureServer(server) {
|
|
44
|
+
// Return a function to ensure our middleware is placed after internal middlewares
|
|
45
|
+
return () => {
|
|
46
|
+
server.middlewares.use(async function rwsdkStaleBundleErrorHandler(err, req, res, next) {
|
|
47
|
+
if (err &&
|
|
48
|
+
typeof err.message === "string" &&
|
|
49
|
+
err.message.includes("new version of the pre-bundle")) {
|
|
50
|
+
log("Caught stale pre-bundle error. Waiting for server to stabilize...");
|
|
51
|
+
startWaitingForStability();
|
|
52
|
+
await stabilityPromise;
|
|
53
|
+
log("Server stabilized. Sending full-reload and redirecting.");
|
|
54
|
+
// Signal the client to do a full page reload.
|
|
55
|
+
server.environments.client.hot.send({
|
|
56
|
+
type: "full-reload",
|
|
57
|
+
});
|
|
58
|
+
// No need to wait further here, the stability promise handled it.
|
|
59
|
+
res.writeHead(307, { Location: req.url });
|
|
60
|
+
res.end();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
next(err);
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import enhancedResolve from "enhanced-resolve";
|
|
2
1
|
import { Plugin } from "vite";
|
|
3
2
|
export declare const ENV_PREDEFINED_IMPORTS: {
|
|
4
3
|
worker: string[];
|
|
5
4
|
ssr: string[];
|
|
6
5
|
client: string[];
|
|
7
6
|
};
|
|
8
|
-
export declare const ENV_RESOLVERS: {
|
|
9
|
-
ssr: enhancedResolve.ResolveFunction;
|
|
10
|
-
worker: enhancedResolve.ResolveFunction;
|
|
11
|
-
client: enhancedResolve.ResolveFunction;
|
|
12
|
-
};
|
|
13
7
|
export declare const knownDepsResolverPlugin: ({ projectRootDir, }: {
|
|
14
8
|
projectRootDir: string;
|
|
15
9
|
}) => Plugin[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
|
-
import enhancedResolve from "enhanced-resolve";
|
|
3
2
|
import { ROOT_DIR } from "../lib/constants.mjs";
|
|
4
3
|
import { ensureAliasArray } from "./ensureAliasArray.mjs";
|
|
4
|
+
import { ENV_RESOLVERS } from "./envResolvers.mjs";
|
|
5
5
|
const log = debug("rwsdk:vite:known-deps-resolver-plugin");
|
|
6
6
|
const KNOWN_PREFIXES = [
|
|
7
7
|
"react",
|
|
@@ -36,17 +36,6 @@ export const ENV_PREDEFINED_IMPORTS = {
|
|
|
36
36
|
"react-server-dom-webpack/client.edge",
|
|
37
37
|
],
|
|
38
38
|
};
|
|
39
|
-
export const ENV_RESOLVERS = {
|
|
40
|
-
ssr: enhancedResolve.create.sync({
|
|
41
|
-
conditionNames: ["workerd", "worker", "edge", "default"],
|
|
42
|
-
}),
|
|
43
|
-
worker: enhancedResolve.create.sync({
|
|
44
|
-
conditionNames: ["react-server", "workerd", "worker", "edge", "default"],
|
|
45
|
-
}),
|
|
46
|
-
client: enhancedResolve.create.sync({
|
|
47
|
-
conditionNames: ["browser", "default"],
|
|
48
|
-
}),
|
|
49
|
-
};
|
|
50
39
|
function resolveKnownImport(id, envName, projectRootDir, isPrefixedImport = false) {
|
|
51
40
|
if (!isPrefixedImport) {
|
|
52
41
|
const isKnownImport = KNOWN_PREFIXES.some((prefix) => id === prefix || id.startsWith(`${prefix}/`));
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
|
-
export declare function linkWorkerBundle({ code, manifestContent, projectRootDir, }: {
|
|
2
|
+
export declare function linkWorkerBundle({ code, manifestContent, projectRootDir, base, }: {
|
|
3
3
|
code: string;
|
|
4
4
|
manifestContent: string;
|
|
5
5
|
projectRootDir: string;
|
|
6
|
+
base?: string;
|
|
6
7
|
}): {
|
|
7
8
|
code: string;
|
|
8
9
|
map: null;
|
|
@@ -4,19 +4,22 @@ import path from "node:path";
|
|
|
4
4
|
import { CLIENT_MANIFEST_RELATIVE_PATH } from "../lib/constants.mjs";
|
|
5
5
|
import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
|
|
6
6
|
const log = debug("rwsdk:vite:linker-plugin");
|
|
7
|
-
export function linkWorkerBundle({ code, manifestContent, projectRootDir, }) {
|
|
7
|
+
export function linkWorkerBundle({ code, manifestContent, projectRootDir, base, }) {
|
|
8
8
|
let newCode = code;
|
|
9
9
|
const manifest = JSON.parse(manifestContent);
|
|
10
10
|
// 1. Replace the manifest placeholder with the actual manifest content.
|
|
11
11
|
log("Injecting manifest into worker bundle");
|
|
12
|
-
newCode = newCode.replace('"__RWSDK_MANIFEST_PLACEHOLDER__"
|
|
12
|
+
newCode = newCode.replace(/['"]__RWSDK_MANIFEST_PLACEHOLDER__['"]/, manifestContent);
|
|
13
13
|
// 2. Replace asset placeholders with their final hashed paths.
|
|
14
14
|
log("Replacing asset placeholders in final worker bundle");
|
|
15
15
|
for (const [key, value] of Object.entries(manifest)) {
|
|
16
16
|
const normalizedKey = normalizeModulePath(key, projectRootDir, {
|
|
17
17
|
isViteStyle: false,
|
|
18
18
|
});
|
|
19
|
-
|
|
19
|
+
// If base is provided, prepend it with the final hashed path.
|
|
20
|
+
// Base is assumed to have a trailing "/".
|
|
21
|
+
const assetPath = (base ? base : "/") + value.file;
|
|
22
|
+
newCode = newCode.replaceAll(`rwsdk_asset:${normalizedKey}`, assetPath);
|
|
20
23
|
}
|
|
21
24
|
// 3. Deprefix any remaining placeholders that were not in the manifest.
|
|
22
25
|
// This handles public assets that don't go through the bundler.
|
|
@@ -28,8 +31,12 @@ export function linkWorkerBundle({ code, manifestContent, projectRootDir, }) {
|
|
|
28
31
|
};
|
|
29
32
|
}
|
|
30
33
|
export const linkerPlugin = ({ projectRootDir, }) => {
|
|
34
|
+
let config;
|
|
31
35
|
return {
|
|
32
36
|
name: "rwsdk:linker",
|
|
37
|
+
configResolved(resolvedConfig) {
|
|
38
|
+
config = resolvedConfig;
|
|
39
|
+
},
|
|
33
40
|
async renderChunk(code) {
|
|
34
41
|
if (this.environment.name !== "worker" ||
|
|
35
42
|
process.env.RWSDK_BUILD_PASS !== "linker") {
|
|
@@ -41,6 +48,7 @@ export const linkerPlugin = ({ projectRootDir, }) => {
|
|
|
41
48
|
code,
|
|
42
49
|
manifestContent,
|
|
43
50
|
projectRootDir,
|
|
51
|
+
base: config.base,
|
|
44
52
|
});
|
|
45
53
|
log("Final worker chunk rendered");
|
|
46
54
|
return result;
|
|
@@ -29,6 +29,21 @@ describe("linkWorkerBundle", () => {
|
|
|
29
29
|
expect(result.code).toContain(`const stylesheet = "/assets/styles.123.css";`);
|
|
30
30
|
expect(result.code).toContain(`const logo = "/assets/logo.abc.svg";`);
|
|
31
31
|
});
|
|
32
|
+
it("should replace asset placeholder with a base + hashed paths from the manifest if base is provided", () => {
|
|
33
|
+
const code = `
|
|
34
|
+
const stylesheet = "rwsdk_asset:/src/styles.css";
|
|
35
|
+
const logo = "rwsdk_asset:/src/logo.svg";
|
|
36
|
+
`;
|
|
37
|
+
const base = "/base/";
|
|
38
|
+
const result = linkWorkerBundle({
|
|
39
|
+
code,
|
|
40
|
+
manifestContent,
|
|
41
|
+
projectRootDir,
|
|
42
|
+
base,
|
|
43
|
+
});
|
|
44
|
+
expect(result.code).toContain(`const stylesheet = "/base/assets/styles.123.css";`);
|
|
45
|
+
expect(result.code).toContain(`const logo = "/base/assets/logo.abc.svg";`);
|
|
46
|
+
});
|
|
32
47
|
it("should deprefix remaining asset placeholders not in the manifest", () => {
|
|
33
48
|
const code = `const publicImg = "rwsdk_asset:/images/photo.jpg";`;
|
|
34
49
|
const result = linkWorkerBundle({
|
|
@@ -18,12 +18,13 @@ import { directivesPlugin } from "./directivesPlugin.mjs";
|
|
|
18
18
|
import { injectVitePreamble } from "./injectVitePreamblePlugin.mjs";
|
|
19
19
|
import { knownDepsResolverPlugin } from "./knownDepsResolverPlugin.mjs";
|
|
20
20
|
import { linkerPlugin } from "./linkerPlugin.mjs";
|
|
21
|
-
import { manifestPlugin } from "./manifestPlugin.mjs";
|
|
22
21
|
import { miniflareHMRPlugin } from "./miniflareHMRPlugin.mjs";
|
|
23
22
|
import { moveStaticAssetsPlugin } from "./moveStaticAssetsPlugin.mjs";
|
|
24
23
|
import { prismaPlugin } from "./prismaPlugin.mjs";
|
|
25
24
|
import { resolveForcedPaths } from "./resolveForcedPaths.mjs";
|
|
26
25
|
import { ssrBridgePlugin } from "./ssrBridgePlugin.mjs";
|
|
26
|
+
import { staleDepRetryPlugin } from "./staleDepRetryPlugin.mjs";
|
|
27
|
+
import { statePlugin } from "./statePlugin.mjs";
|
|
27
28
|
import { transformJsxScriptTagsPlugin } from "./transformJsxScriptTagsPlugin.mjs";
|
|
28
29
|
import { useClientLookupPlugin } from "./useClientLookupPlugin.mjs";
|
|
29
30
|
import { useServerLookupPlugin } from "./useServerLookupPlugin.mjs";
|
|
@@ -78,13 +79,13 @@ export const redwoodPlugin = async (options = {}) => {
|
|
|
78
79
|
!(await pathExists(resolve(projectRootDir, ".wrangler"))) &&
|
|
79
80
|
(await hasPkgScript(projectRootDir, "dev:init"))) {
|
|
80
81
|
console.log("🚀 Project has no .wrangler directory yet, assuming fresh install: running `npm run dev:init`...");
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
stdio: ["ignore", "inherit", "inherit"],
|
|
85
|
-
}) `npm run dev:init`;
|
|
82
|
+
// @ts-ignore
|
|
83
|
+
$.verbose = true;
|
|
84
|
+
await $ `npm run dev:init`;
|
|
86
85
|
}
|
|
87
86
|
return [
|
|
87
|
+
staleDepRetryPlugin(),
|
|
88
|
+
statePlugin({ projectRootDir }),
|
|
88
89
|
devServerTimingPlugin(),
|
|
89
90
|
devServerConstantPlugin(),
|
|
90
91
|
directiveModulesDevPlugin({
|
|
@@ -144,9 +145,6 @@ export const redwoodPlugin = async (options = {}) => {
|
|
|
144
145
|
clientEntryPoints,
|
|
145
146
|
projectRootDir,
|
|
146
147
|
}),
|
|
147
|
-
manifestPlugin({
|
|
148
|
-
projectRootDir,
|
|
149
|
-
}),
|
|
150
148
|
moveStaticAssetsPlugin({ rootDir: projectRootDir }),
|
|
151
149
|
prismaPlugin({ projectRootDir }),
|
|
152
150
|
linkerPlugin({ projectRootDir }),
|