rwsdk 1.0.0-beta.2-test.20250930092748 → 1.0.0-beta.21
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 +7 -4
- package/dist/lib/e2e/constants.d.mts +14 -0
- package/dist/lib/e2e/constants.mjs +74 -0
- package/dist/lib/e2e/dev.mjs +21 -34
- 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 +196 -119
- 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 +52 -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/getViteEsbuild.mjs +2 -1
- 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/moveStaticAssetsPlugin.mjs +14 -4
- package/dist/vite/redwoodPlugin.mjs +7 -9
- package/dist/vite/runDirectivesScan.mjs +59 -14
- 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
package/dist/lib/constants.d.mts
CHANGED
|
@@ -7,5 +7,6 @@ export declare const VENDOR_CLIENT_BARREL_PATH: string;
|
|
|
7
7
|
export declare const VENDOR_SERVER_BARREL_PATH: string;
|
|
8
8
|
export declare const VENDOR_CLIENT_BARREL_EXPORT_PATH = "rwsdk/__vendor_client_barrel";
|
|
9
9
|
export declare const VENDOR_SERVER_BARREL_EXPORT_PATH = "rwsdk/__vendor_server_barrel";
|
|
10
|
+
export declare const RW_STATE_EXPORT_PATH = "rwsdk/__state";
|
|
10
11
|
export declare const INTERMEDIATE_SSR_BRIDGE_PATH: string;
|
|
11
12
|
export declare const CLIENT_MANIFEST_RELATIVE_PATH: string;
|
package/dist/lib/constants.mjs
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { fileURLToPath } from "url";
|
|
2
|
+
import { dirname, resolve } from "path";
|
|
3
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = dirname(__filename);
|
|
3
5
|
export const ROOT_DIR = resolve(__dirname, "..", "..");
|
|
4
6
|
export const SRC_DIR = resolve(ROOT_DIR, "src");
|
|
5
7
|
export const DIST_DIR = resolve(ROOT_DIR, "dist");
|
|
6
8
|
export const VITE_DIR = resolve(ROOT_DIR, "src", "vite");
|
|
7
9
|
export const INTERMEDIATES_OUTPUT_DIR = resolve(DIST_DIR, "__intermediate_builds");
|
|
8
|
-
export const VENDOR_CLIENT_BARREL_PATH =
|
|
9
|
-
export const VENDOR_SERVER_BARREL_PATH =
|
|
10
|
+
export const VENDOR_CLIENT_BARREL_PATH = resolve(INTERMEDIATES_OUTPUT_DIR, "rwsdk-vendor-client-barrel.js");
|
|
11
|
+
export const VENDOR_SERVER_BARREL_PATH = resolve(INTERMEDIATES_OUTPUT_DIR, "rwsdk-vendor-server-barrel.js");
|
|
10
12
|
export const VENDOR_CLIENT_BARREL_EXPORT_PATH = "rwsdk/__vendor_client_barrel";
|
|
11
13
|
export const VENDOR_SERVER_BARREL_EXPORT_PATH = "rwsdk/__vendor_server_barrel";
|
|
14
|
+
export const RW_STATE_EXPORT_PATH = "rwsdk/__state";
|
|
12
15
|
export const INTERMEDIATE_SSR_BRIDGE_PATH = resolve(INTERMEDIATES_OUTPUT_DIR, "ssr", "ssr_bridge.js");
|
|
13
16
|
export const CLIENT_MANIFEST_RELATIVE_PATH = resolve("dist", "client", ".vite", "manifest.json");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const IS_CI: boolean;
|
|
2
|
+
export declare const IS_DEBUG_MODE: boolean;
|
|
3
|
+
export declare const SETUP_PLAYGROUND_ENV_TIMEOUT: number;
|
|
4
|
+
export declare const DEPLOYMENT_TIMEOUT: number;
|
|
5
|
+
export declare const DEPLOYMENT_MIN_TRIES: number;
|
|
6
|
+
export declare const DEPLOYMENT_CHECK_TIMEOUT: number;
|
|
7
|
+
export declare const PUPPETEER_TIMEOUT: number;
|
|
8
|
+
export declare const HYDRATION_TIMEOUT: number;
|
|
9
|
+
export declare const DEV_SERVER_TIMEOUT: number;
|
|
10
|
+
export declare const DEV_SERVER_MIN_TRIES: number;
|
|
11
|
+
export declare const SETUP_WAIT_TIMEOUT: number;
|
|
12
|
+
export declare const TEST_MAX_RETRIES: number;
|
|
13
|
+
export declare const TEST_MAX_RETRIES_PER_CODE: number;
|
|
14
|
+
export declare const INSTALL_DEPENDENCIES_RETRIES: number;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const IS_CI = !!((process.env.CI && !process.env.NOT_CI) ||
|
|
2
|
+
process.env.GITHUB_ACTIONS ||
|
|
3
|
+
process.env.GITLAB_CI ||
|
|
4
|
+
process.env.CIRCLECI ||
|
|
5
|
+
process.env.TRAVIS ||
|
|
6
|
+
process.env.JENKINS_URL ||
|
|
7
|
+
process.env.NETLIFY);
|
|
8
|
+
export const IS_DEBUG_MODE = process.env.RWSDK_E2E_DEBUG
|
|
9
|
+
? process.env.RWSDK_E2E_DEBUG === "true"
|
|
10
|
+
: !IS_CI;
|
|
11
|
+
export const SETUP_PLAYGROUND_ENV_TIMEOUT = process.env
|
|
12
|
+
.RWSDK_SETUP_PLAYGROUND_ENV_TIMEOUT
|
|
13
|
+
? parseInt(process.env.RWSDK_SETUP_PLAYGROUND_ENV_TIMEOUT, 10)
|
|
14
|
+
: IS_DEBUG_MODE
|
|
15
|
+
? 10 * 60 * 1000
|
|
16
|
+
: 15 * 60 * 1000;
|
|
17
|
+
export const DEPLOYMENT_TIMEOUT = process.env.RWSDK_DEPLOYMENT_TIMEOUT
|
|
18
|
+
? parseInt(process.env.RWSDK_DEPLOYMENT_TIMEOUT, 10)
|
|
19
|
+
: IS_DEBUG_MODE
|
|
20
|
+
? 5 * 30 * 1000
|
|
21
|
+
: 5 * 60 * 1000;
|
|
22
|
+
export const DEPLOYMENT_MIN_TRIES = process.env.RWSDK_DEPLOYMENT_MIN_TRIES
|
|
23
|
+
? parseInt(process.env.RWSDK_DEPLOYMENT_MIN_TRIES, 10)
|
|
24
|
+
: IS_DEBUG_MODE
|
|
25
|
+
? 1
|
|
26
|
+
: 5;
|
|
27
|
+
export const DEPLOYMENT_CHECK_TIMEOUT = process.env
|
|
28
|
+
.RWSDK_DEPLOYMENT_CHECK_TIMEOUT
|
|
29
|
+
? parseInt(process.env.RWSDK_DEPLOYMENT_CHECK_TIMEOUT, 10)
|
|
30
|
+
: IS_DEBUG_MODE
|
|
31
|
+
? 30 * 1000
|
|
32
|
+
: 5 * 60 * 1000;
|
|
33
|
+
export const PUPPETEER_TIMEOUT = process.env.RWSDK_PUPPETEER_TIMEOUT
|
|
34
|
+
? parseInt(process.env.RWSDK_PUPPETEER_TIMEOUT, 10)
|
|
35
|
+
: IS_DEBUG_MODE
|
|
36
|
+
? 30 * 1000
|
|
37
|
+
: 60 * 1000 * 2;
|
|
38
|
+
export const HYDRATION_TIMEOUT = process.env.RWSDK_HYDRATION_TIMEOUT
|
|
39
|
+
? parseInt(process.env.RWSDK_HYDRATION_TIMEOUT, 10)
|
|
40
|
+
: IS_DEBUG_MODE
|
|
41
|
+
? 2000
|
|
42
|
+
: 5000;
|
|
43
|
+
export const DEV_SERVER_TIMEOUT = process.env.RWSDK_DEV_SERVER_TIMEOUT
|
|
44
|
+
? parseInt(process.env.RWSDK_DEV_SERVER_TIMEOUT, 10)
|
|
45
|
+
: IS_DEBUG_MODE
|
|
46
|
+
? 60 * 1000
|
|
47
|
+
: 5 * 60 * 1000;
|
|
48
|
+
export const DEV_SERVER_MIN_TRIES = process.env.RWSDK_DEV_SERVER_MIN_TRIES
|
|
49
|
+
? parseInt(process.env.RWSDK_DEV_SERVER_MIN_TRIES, 10)
|
|
50
|
+
: IS_DEBUG_MODE
|
|
51
|
+
? 1
|
|
52
|
+
: 5;
|
|
53
|
+
export const SETUP_WAIT_TIMEOUT = process.env.RWSDK_SETUP_WAIT_TIMEOUT
|
|
54
|
+
? parseInt(process.env.RWSDK_SETUP_WAIT_TIMEOUT, 10)
|
|
55
|
+
: IS_DEBUG_MODE
|
|
56
|
+
? 60 * 1000
|
|
57
|
+
: 10 * 60 * 1000;
|
|
58
|
+
export const TEST_MAX_RETRIES = process.env.RWSDK_TEST_MAX_RETRIES
|
|
59
|
+
? parseInt(process.env.RWSDK_TEST_MAX_RETRIES, 10)
|
|
60
|
+
: IS_DEBUG_MODE
|
|
61
|
+
? 1
|
|
62
|
+
: 10;
|
|
63
|
+
export const TEST_MAX_RETRIES_PER_CODE = process.env
|
|
64
|
+
.RWSDK_TEST_MAX_RETRIES_PER_CODE
|
|
65
|
+
? parseInt(process.env.RWSDK_TEST_MAX_RETRIES_PER_CODE, 10)
|
|
66
|
+
: IS_DEBUG_MODE
|
|
67
|
+
? 0
|
|
68
|
+
: 6;
|
|
69
|
+
export const INSTALL_DEPENDENCIES_RETRIES = process.env
|
|
70
|
+
.RWSDK_INSTALL_DEPENDENCIES_RETRIES
|
|
71
|
+
? parseInt(process.env.RWSDK_INSTALL_DEPENDENCIES_RETRIES, 10)
|
|
72
|
+
: IS_DEBUG_MODE
|
|
73
|
+
? 1
|
|
74
|
+
: 10;
|
package/dist/lib/e2e/dev.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
2
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
3
|
-
import { $ } from "../../lib/$.mjs";
|
|
3
|
+
import { $, $sh } from "../../lib/$.mjs";
|
|
4
4
|
import { poll } from "./poll.mjs";
|
|
5
5
|
const DEV_SERVER_CHECK_TIMEOUT = process.env.RWSDK_DEV_SERVER_CHECK_TIMEOUT
|
|
6
6
|
? parseInt(process.env.RWSDK_DEV_SERVER_CHECK_TIMEOUT, 10)
|
|
@@ -21,43 +21,30 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
console.log("Stopping development server...");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// Wait for the process to terminate with a timeout
|
|
32
|
-
const terminationTimeout = 5000; // 5 seconds
|
|
33
|
-
const processExitPromise = devProcess.catch(() => {
|
|
34
|
-
// We expect this promise to reject when the process is killed,
|
|
35
|
-
// so we catch and ignore the error.
|
|
36
|
-
});
|
|
37
|
-
const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(undefined), terminationTimeout));
|
|
38
|
-
await Promise.race([processExitPromise, timeoutPromise]);
|
|
39
|
-
// Check if the process is still alive. We can't reliably check exitCode
|
|
40
|
-
// on a detached process, so we try sending a signal 0, which errors
|
|
41
|
-
// if the process doesn't exist.
|
|
42
|
-
let isAlive = true;
|
|
43
|
-
try {
|
|
44
|
-
// Sending signal 0 doesn't kill the process, but checks if it exists
|
|
45
|
-
process.kill(-devProcess.pid, 0);
|
|
46
|
-
}
|
|
47
|
-
catch (e) {
|
|
48
|
-
isAlive = false;
|
|
24
|
+
if (process.platform === "win32") {
|
|
25
|
+
try {
|
|
26
|
+
await $sh(`taskkill /pid ${devProcess.pid} /f /t`);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
log("Failed to kill process tree with taskkill:", err);
|
|
30
|
+
}
|
|
49
31
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
32
|
+
else {
|
|
33
|
+
// On Unix-like systems, we kill the entire process group by sending a signal
|
|
34
|
+
// to the negative PID. This is the equivalent of the `/t` flag for `taskkill` on Windows.
|
|
35
|
+
// This relies on `detached: true` being set in the execa options, which makes
|
|
36
|
+
// the child process the leader of a new process group.
|
|
54
37
|
try {
|
|
55
38
|
process.kill(-devProcess.pid, "SIGKILL");
|
|
56
39
|
}
|
|
57
40
|
catch (e) {
|
|
58
|
-
log("
|
|
41
|
+
log("Failed to kill process group. This may happen if the process already exited. %O", e);
|
|
59
42
|
}
|
|
60
43
|
}
|
|
44
|
+
await devProcess.catch(() => {
|
|
45
|
+
// We expect this promise to reject when the process is killed,
|
|
46
|
+
// so we catch and ignore the error.
|
|
47
|
+
});
|
|
61
48
|
console.log("Development server stopped");
|
|
62
49
|
};
|
|
63
50
|
try {
|
|
@@ -88,8 +75,9 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
88
75
|
// Use the provided cwd if available
|
|
89
76
|
devProcess = $({
|
|
90
77
|
all: true,
|
|
91
|
-
detached: true, //
|
|
92
|
-
cleanup:
|
|
78
|
+
detached: true, // Re-enable for reliable process cleanup
|
|
79
|
+
cleanup: true, // Let execa handle cleanup
|
|
80
|
+
forceKillAfterTimeout: 2000, // Force kill if graceful shutdown fails
|
|
93
81
|
cwd: cwd || process.cwd(), // Use provided directory or current directory
|
|
94
82
|
env, // Pass the updated environment variables
|
|
95
83
|
stdio: "pipe", // Ensure streams are piped
|
|
@@ -109,7 +97,6 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
109
97
|
// Listen for all output to get the URL
|
|
110
98
|
const handleOutput = (data, source) => {
|
|
111
99
|
const output = data.toString();
|
|
112
|
-
console.log(output);
|
|
113
100
|
allOutput += output; // Accumulate all output
|
|
114
101
|
log("Received output from %s: %s", source, output.replace(/\n/g, "\\n"));
|
|
115
102
|
if (!url) {
|
|
@@ -3,7 +3,7 @@ import { PackageManager } from "./types.mjs";
|
|
|
3
3
|
/**
|
|
4
4
|
* Copy project to a temporary directory with a unique name
|
|
5
5
|
*/
|
|
6
|
-
export declare function copyProjectToTempDir(projectDir: string, resourceUniqueKey: string, packageManager?: PackageManager, monorepoRoot?: string): Promise<{
|
|
6
|
+
export declare function copyProjectToTempDir(projectDir: string, resourceUniqueKey: string, packageManager?: PackageManager, monorepoRoot?: string, installDependenciesRetries?: number): Promise<{
|
|
7
7
|
tempDir: tmp.DirectoryResult;
|
|
8
8
|
targetDir: string;
|
|
9
9
|
workerName: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
1
2
|
import debug from "debug";
|
|
2
3
|
import { copy, pathExists } from "fs-extra";
|
|
3
4
|
import ignore from "ignore";
|
|
@@ -8,8 +9,25 @@ import { basename, join, relative, resolve } from "path";
|
|
|
8
9
|
import tmp from "tmp-promise";
|
|
9
10
|
import { $ } from "../../lib/$.mjs";
|
|
10
11
|
import { ROOT_DIR } from "../constants.mjs";
|
|
12
|
+
import { INSTALL_DEPENDENCIES_RETRIES, IS_CI } from "./constants.mjs";
|
|
11
13
|
import { retry } from "./retry.mjs";
|
|
12
14
|
const log = debug("rwsdk:e2e:environment");
|
|
15
|
+
const IS_CACHE_ENABLED = process.env.RWSDK_E2E_CACHE
|
|
16
|
+
? process.env.RWSDK_E2E_CACHE === "1"
|
|
17
|
+
: !IS_CI;
|
|
18
|
+
if (IS_CACHE_ENABLED) {
|
|
19
|
+
log("E2E test caching is enabled.");
|
|
20
|
+
}
|
|
21
|
+
const getTempDir = async () => {
|
|
22
|
+
return tmp.dir({ unsafeCleanup: true });
|
|
23
|
+
};
|
|
24
|
+
function slugify(str) {
|
|
25
|
+
return str
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
.replace(/[^a-z0-9-]/g, "-")
|
|
28
|
+
.replace(/--+/g, "-")
|
|
29
|
+
.replace(/^-|-$/g, "");
|
|
30
|
+
}
|
|
13
31
|
const createSdkTarball = async () => {
|
|
14
32
|
const existingTarballPath = process.env.RWSKD_SMOKE_TEST_TARBALL_PATH;
|
|
15
33
|
if (existingTarballPath) {
|
|
@@ -24,15 +42,27 @@ const createSdkTarball = async () => {
|
|
|
24
42
|
}, // No-op cleanup
|
|
25
43
|
};
|
|
26
44
|
}
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
// Create a temporary directory to receive the tarball, ensuring a stable path.
|
|
46
|
+
const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "rwsdk-tarball-"));
|
|
47
|
+
await $({
|
|
48
|
+
cwd: ROOT_DIR,
|
|
49
|
+
stdio: "pipe",
|
|
50
|
+
}) `npm pack --pack-destination=${tempDir}`;
|
|
51
|
+
// We need to determine the tarball's name, as it's version-dependent.
|
|
52
|
+
// Running `npm pack --dry-run` gives us the filename without creating a file.
|
|
53
|
+
const packDryRun = await $({
|
|
54
|
+
cwd: ROOT_DIR,
|
|
55
|
+
stdio: "pipe",
|
|
56
|
+
}) `npm pack --dry-run`;
|
|
57
|
+
const tarballName = packDryRun.stdout?.trim();
|
|
58
|
+
const tarballPath = path.join(tempDir, tarballName);
|
|
59
|
+
if (!fs.existsSync(tarballPath)) {
|
|
60
|
+
throw new Error(`Tarball was not created in the expected location: ${tarballPath}`);
|
|
61
|
+
}
|
|
62
|
+
log(`📦 Created tarball in stable temp location: ${tarballPath}`);
|
|
31
63
|
const cleanupTarball = async () => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
await fs.promises.rm(tarballPath, { force: true });
|
|
35
|
-
}
|
|
64
|
+
log(`🧹 Cleaning up tarball directory: ${tempDir}`);
|
|
65
|
+
await fs.promises.rm(tempDir, { recursive: true, force: true });
|
|
36
66
|
};
|
|
37
67
|
return { tarballPath, cleanupTarball };
|
|
38
68
|
};
|
|
@@ -46,17 +76,16 @@ const setTarballDependency = async (targetDir, tarballName) => {
|
|
|
46
76
|
/**
|
|
47
77
|
* Copy project to a temporary directory with a unique name
|
|
48
78
|
*/
|
|
49
|
-
export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager, monorepoRoot) {
|
|
79
|
+
export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager, monorepoRoot, installDependenciesRetries) {
|
|
50
80
|
const { tarballPath, cleanupTarball } = await createSdkTarball();
|
|
51
81
|
try {
|
|
52
82
|
log("Creating temporary directory for project");
|
|
53
|
-
|
|
54
|
-
const tempDir = await tmp.dir({ unsafeCleanup: true });
|
|
83
|
+
const tempDir = await getTempDir();
|
|
55
84
|
// Determine the source directory to copy from
|
|
56
85
|
const sourceDir = monorepoRoot || projectDir;
|
|
57
86
|
// Create unique project directory name
|
|
58
87
|
const originalDirName = basename(sourceDir);
|
|
59
|
-
const workerName = `${originalDirName}-test-${resourceUniqueKey}`;
|
|
88
|
+
const workerName = `${slugify(originalDirName)}-test-${resourceUniqueKey}`;
|
|
60
89
|
const tempCopyRoot = resolve(tempDir.path, workerName);
|
|
61
90
|
// If it's a monorepo, the targetDir for commands is a subdirectory
|
|
62
91
|
const targetDir = monorepoRoot
|
|
@@ -112,7 +141,9 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
|
|
|
112
141
|
const workspaces = rwsdkWs.workspaces;
|
|
113
142
|
if (packageManager === "pnpm") {
|
|
114
143
|
const pnpmWsPath = join(tempCopyRoot, "pnpm-workspace.yaml");
|
|
115
|
-
const pnpmWsConfig = `packages:\n${workspaces
|
|
144
|
+
const pnpmWsConfig = `packages:\n${workspaces
|
|
145
|
+
.map((w) => ` - '${w}'`)
|
|
146
|
+
.join("\n")}\n`;
|
|
116
147
|
await fs.promises.writeFile(pnpmWsPath, pnpmWsConfig);
|
|
117
148
|
log("Created pnpm-workspace.yaml");
|
|
118
149
|
}
|
|
@@ -130,7 +161,6 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
|
|
|
130
161
|
log("⚙️ Configuring temp project to not use frozen lockfile...");
|
|
131
162
|
const npmrcPath = join(targetDir, ".npmrc");
|
|
132
163
|
await fs.promises.writeFile(npmrcPath, "frozen-lockfile=false\n");
|
|
133
|
-
// For yarn, create .yarnrc.yml to disable PnP and allow lockfile changes
|
|
134
164
|
if (packageManager === "yarn") {
|
|
135
165
|
const yarnrcPath = join(targetDir, ".yarnrc.yml");
|
|
136
166
|
const yarnCacheDir = path.join(os.tmpdir(), "yarn-cache");
|
|
@@ -144,11 +174,19 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
|
|
|
144
174
|
await fs.promises.writeFile(yarnrcPath, yarnConfig);
|
|
145
175
|
log("Created .yarnrc.yml to allow lockfile changes for yarn");
|
|
146
176
|
}
|
|
177
|
+
if (packageManager === "yarn-classic") {
|
|
178
|
+
const yarnrcPath = join(targetDir, ".yarnrc");
|
|
179
|
+
const yarnCacheDir = path.join(os.tmpdir(), "yarn-classic-cache");
|
|
180
|
+
await fs.promises.mkdir(yarnCacheDir, { recursive: true });
|
|
181
|
+
const yarnConfig = `cache-folder "${yarnCacheDir}"`;
|
|
182
|
+
await fs.promises.writeFile(yarnrcPath, yarnConfig);
|
|
183
|
+
log("Created .yarnrc with cache-folder for yarn-classic");
|
|
184
|
+
}
|
|
147
185
|
await setTarballDependency(targetDir, tarballFilename);
|
|
148
186
|
// Install dependencies in the target directory
|
|
149
187
|
const installDir = monorepoRoot ? tempCopyRoot : targetDir;
|
|
150
|
-
await retry(() => installDependencies(installDir, packageManager), {
|
|
151
|
-
retries:
|
|
188
|
+
await retry(() => installDependencies(installDir, packageManager, projectDir, monorepoRoot), {
|
|
189
|
+
retries: INSTALL_DEPENDENCIES_RETRIES,
|
|
152
190
|
delay: 1000,
|
|
153
191
|
});
|
|
154
192
|
// Return the environment details
|
|
@@ -158,7 +196,42 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
|
|
|
158
196
|
await cleanupTarball();
|
|
159
197
|
}
|
|
160
198
|
}
|
|
161
|
-
async function installDependencies(targetDir, packageManager = "pnpm") {
|
|
199
|
+
async function installDependencies(targetDir, packageManager = "pnpm", projectDir, monorepoRoot) {
|
|
200
|
+
if (IS_CACHE_ENABLED) {
|
|
201
|
+
// Generate a checksum of the SDK's dist directory to factor into the cache key
|
|
202
|
+
const { stdout: sdkDistChecksum } = await $("find . -type f | sort | md5sum", {
|
|
203
|
+
shell: true,
|
|
204
|
+
cwd: path.join(ROOT_DIR, "dist"),
|
|
205
|
+
});
|
|
206
|
+
const projectIdentifier = monorepoRoot
|
|
207
|
+
? `${monorepoRoot}-${projectDir}`
|
|
208
|
+
: projectDir;
|
|
209
|
+
const projectHash = createHash("md5")
|
|
210
|
+
.update(`${projectIdentifier}-${sdkDistChecksum}`)
|
|
211
|
+
.digest("hex")
|
|
212
|
+
.substring(0, 8);
|
|
213
|
+
const cacheDirName = monorepoRoot
|
|
214
|
+
? basename(monorepoRoot)
|
|
215
|
+
: basename(projectDir);
|
|
216
|
+
const cacheRoot = path.join(os.tmpdir(), "rwsdk-e2e-cache", `${cacheDirName}-${projectHash}`);
|
|
217
|
+
const nodeModulesCachePath = path.join(cacheRoot, "node_modules");
|
|
218
|
+
if (await pathExists(nodeModulesCachePath)) {
|
|
219
|
+
console.log(`✅ CACHE HIT for source "${projectIdentifier}": Found cached node_modules. Hard-linking from ${nodeModulesCachePath}`);
|
|
220
|
+
try {
|
|
221
|
+
// Use cp -al for a fast, hardlink-based copy
|
|
222
|
+
await $("cp", ["-al", nodeModulesCachePath, join(targetDir, "node_modules")], { stdio: "pipe" });
|
|
223
|
+
console.log(`✅ Hardlink copy created successfully.`);
|
|
224
|
+
}
|
|
225
|
+
catch (e) {
|
|
226
|
+
console.warn(`⚠️ Hardlink copy failed, falling back to full copy. Error: ${e.message}`);
|
|
227
|
+
// Fallback to a regular copy if hardlinking fails (e.g., cross-device)
|
|
228
|
+
await copy(nodeModulesCachePath, join(targetDir, "node_modules"));
|
|
229
|
+
console.log(`✅ Full copy created successfully.`);
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
console.log(`ℹ️ CACHE MISS for source "${projectIdentifier}": No cached node_modules found at ${nodeModulesCachePath}. Proceeding with installation.`);
|
|
234
|
+
}
|
|
162
235
|
console.log(`📦 Installing project dependencies in ${targetDir} using ${packageManager}...`);
|
|
163
236
|
try {
|
|
164
237
|
// Clean up any pre-existing node_modules and lockfiles
|
|
@@ -185,7 +258,7 @@ async function installDependencies(targetDir, packageManager = "pnpm") {
|
|
|
185
258
|
}
|
|
186
259
|
else if (packageManager === "yarn-classic") {
|
|
187
260
|
log(`Preparing yarn@1.22.19 with corepack...`);
|
|
188
|
-
await $("corepack", ["prepare", "yarn@1.
|
|
261
|
+
await $("corepack", ["prepare", "yarn@1.x", "--activate"], {
|
|
189
262
|
cwd: targetDir,
|
|
190
263
|
stdio: "pipe",
|
|
191
264
|
});
|
|
@@ -210,6 +283,33 @@ async function installDependencies(targetDir, packageManager = "pnpm") {
|
|
|
210
283
|
},
|
|
211
284
|
});
|
|
212
285
|
console.log("✅ Dependencies installed successfully");
|
|
286
|
+
// After successful install, populate the cache if enabled
|
|
287
|
+
if (IS_CACHE_ENABLED) {
|
|
288
|
+
// Re-calculate cache path to be safe
|
|
289
|
+
const { stdout: sdkDistChecksum } = await $("find . -type f | sort | md5sum", {
|
|
290
|
+
shell: true,
|
|
291
|
+
cwd: path.join(ROOT_DIR, "dist"),
|
|
292
|
+
});
|
|
293
|
+
const projectIdentifier = monorepoRoot
|
|
294
|
+
? `${monorepoRoot}-${projectDir}`
|
|
295
|
+
: projectDir;
|
|
296
|
+
const projectHash = createHash("md5")
|
|
297
|
+
.update(`${projectIdentifier}-${sdkDistChecksum}`)
|
|
298
|
+
.digest("hex")
|
|
299
|
+
.substring(0, 8);
|
|
300
|
+
const cacheDirName = monorepoRoot
|
|
301
|
+
? basename(monorepoRoot)
|
|
302
|
+
: basename(projectDir);
|
|
303
|
+
const cacheRoot = path.join(os.tmpdir(), "rwsdk-e2e-cache", `${cacheDirName}-${projectHash}`);
|
|
304
|
+
const nodeModulesCachePath = path.join(cacheRoot, "node_modules");
|
|
305
|
+
console.log(`Caching node_modules to ${nodeModulesCachePath} for future runs...`);
|
|
306
|
+
// Ensure parent directory exists
|
|
307
|
+
await fs.promises.mkdir(path.dirname(nodeModulesCachePath), {
|
|
308
|
+
recursive: true,
|
|
309
|
+
});
|
|
310
|
+
await copy(join(targetDir, "node_modules"), nodeModulesCachePath);
|
|
311
|
+
console.log(`✅ node_modules cached successfully.`);
|
|
312
|
+
}
|
|
213
313
|
// Log installation details at debug level
|
|
214
314
|
if (result.stdout) {
|
|
215
315
|
log(`${packageManager} install output: %s`, result.stdout);
|
package/dist/lib/e2e/index.d.mts
CHANGED
package/dist/lib/e2e/index.mjs
CHANGED
package/dist/lib/e2e/poll.d.mts
CHANGED
|
@@ -4,5 +4,5 @@ export interface PollOptions {
|
|
|
4
4
|
minTries: number;
|
|
5
5
|
onRetry?: (error: unknown, tries: number) => void;
|
|
6
6
|
}
|
|
7
|
-
export declare function poll(fn: () => Promise<boolean>, options?: Partial<PollOptions>): Promise<void>;
|
|
7
|
+
export declare function poll(fn: () => boolean | Promise<boolean>, options?: Partial<PollOptions>): Promise<void>;
|
|
8
8
|
export declare function pollValue<T>(fn: () => Promise<T>, options?: Partial<PollOptions>): Promise<T>;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { type Browser, type Page } from "puppeteer-core";
|
|
2
2
|
import { test } from "vitest";
|
|
3
|
+
import { DEPLOYMENT_CHECK_TIMEOUT, DEPLOYMENT_MIN_TRIES, DEPLOYMENT_TIMEOUT, DEV_SERVER_MIN_TRIES, DEV_SERVER_TIMEOUT, HYDRATION_TIMEOUT, INSTALL_DEPENDENCIES_RETRIES, PUPPETEER_TIMEOUT, SETUP_PLAYGROUND_ENV_TIMEOUT, SETUP_WAIT_TIMEOUT, TEST_MAX_RETRIES, TEST_MAX_RETRIES_PER_CODE } from "./constants.mjs";
|
|
3
4
|
export type { Browser, Page } from "puppeteer-core";
|
|
5
|
+
export { DEPLOYMENT_CHECK_TIMEOUT, DEPLOYMENT_MIN_TRIES, DEPLOYMENT_TIMEOUT, DEV_SERVER_MIN_TRIES, DEV_SERVER_TIMEOUT, HYDRATION_TIMEOUT, INSTALL_DEPENDENCIES_RETRIES, PUPPETEER_TIMEOUT, SETUP_PLAYGROUND_ENV_TIMEOUT, SETUP_WAIT_TIMEOUT, TEST_MAX_RETRIES, TEST_MAX_RETRIES_PER_CODE, };
|
|
4
6
|
interface DevServerInstance {
|
|
5
7
|
url: string;
|
|
8
|
+
projectDir: string;
|
|
6
9
|
stopDev: () => Promise<void>;
|
|
7
10
|
}
|
|
8
11
|
interface DeploymentInstance {
|
|
@@ -34,6 +37,11 @@ export interface SetupPlaygroundEnvironmentOptions {
|
|
|
34
37
|
* @default true
|
|
35
38
|
*/
|
|
36
39
|
deploy?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether to automatically start the dev server.
|
|
42
|
+
* @default true
|
|
43
|
+
*/
|
|
44
|
+
autoStartDevServer?: boolean;
|
|
37
45
|
}
|
|
38
46
|
/**
|
|
39
47
|
* A Vitest hook that sets up a playground environment for a test file.
|
|
@@ -41,17 +49,29 @@ export interface SetupPlaygroundEnvironmentOptions {
|
|
|
41
49
|
* and installs dependencies using a tarball of the SDK.
|
|
42
50
|
* This ensures that tests run in a clean, isolated environment.
|
|
43
51
|
*/
|
|
44
|
-
export declare function setupPlaygroundEnvironment(options
|
|
52
|
+
export declare function setupPlaygroundEnvironment(options: SetupPlaygroundEnvironmentOptions | string): void;
|
|
45
53
|
/**
|
|
46
54
|
* Creates a dev server instance using the shared playground environment.
|
|
47
55
|
* Automatically registers cleanup to run after the test.
|
|
48
56
|
*/
|
|
49
|
-
export declare function createDevServer(
|
|
57
|
+
export declare function createDevServer(): {
|
|
58
|
+
projectDir: string;
|
|
59
|
+
start: () => Promise<DevServerInstance>;
|
|
60
|
+
};
|
|
50
61
|
/**
|
|
51
62
|
* Creates a deployment instance using the shared playground environment.
|
|
52
63
|
* Automatically registers cleanup to run after the test.
|
|
53
64
|
*/
|
|
54
|
-
export declare function createDeployment(
|
|
65
|
+
export declare function createDeployment(): {
|
|
66
|
+
projectDir: string;
|
|
67
|
+
start: () => Promise<{
|
|
68
|
+
url: string;
|
|
69
|
+
workerName: string;
|
|
70
|
+
resourceUniqueKey: string;
|
|
71
|
+
projectDir: string;
|
|
72
|
+
cleanup: () => Promise<void>;
|
|
73
|
+
}>;
|
|
74
|
+
};
|
|
55
75
|
/**
|
|
56
76
|
* Executes a test function with a retry mechanism for specific error codes.
|
|
57
77
|
* @param name - The name of the test, used for logging.
|
|
@@ -61,13 +81,23 @@ export declare function createDeployment(projectDir: string): Promise<Deployment
|
|
|
61
81
|
* called automatically on failure.
|
|
62
82
|
*/
|
|
63
83
|
export declare function runTestWithRetries(name: string, attemptFn: () => Promise<void>): Promise<void>;
|
|
84
|
+
type SDKRunner = (name: string, testLogic: (context: {
|
|
85
|
+
browser: Browser;
|
|
86
|
+
page: Page;
|
|
87
|
+
projectDir: string;
|
|
88
|
+
}) => Promise<void>) => void;
|
|
64
89
|
declare function createTestRunner(testFn: (typeof test | typeof test.only)["concurrent"], envType: "dev" | "deploy"): (name: string, testLogic: (context: {
|
|
65
90
|
devServer?: DevServerInstance;
|
|
66
91
|
deployment?: DeploymentInstance;
|
|
67
92
|
browser: Browser;
|
|
68
93
|
page: Page;
|
|
69
94
|
url: string;
|
|
95
|
+
projectDir: string;
|
|
70
96
|
}) => Promise<void>) => void;
|
|
97
|
+
export declare const testSDK: SDKRunner & {
|
|
98
|
+
only: SDKRunner;
|
|
99
|
+
skip: typeof test.skip;
|
|
100
|
+
};
|
|
71
101
|
/**
|
|
72
102
|
* High-level test wrapper for dev server tests.
|
|
73
103
|
* Automatically skips if RWSDK_SKIP_DEV=1
|
|
@@ -81,6 +111,7 @@ export declare namespace testDev {
|
|
|
81
111
|
browser: Browser;
|
|
82
112
|
page: Page;
|
|
83
113
|
url: string;
|
|
114
|
+
projectDir: string;
|
|
84
115
|
}) => Promise<void>) => void;
|
|
85
116
|
}
|
|
86
117
|
/**
|
|
@@ -96,6 +127,7 @@ export declare namespace testDeploy {
|
|
|
96
127
|
browser: Browser;
|
|
97
128
|
page: Page;
|
|
98
129
|
url: string;
|
|
130
|
+
projectDir: string;
|
|
99
131
|
}) => Promise<void>) => void;
|
|
100
132
|
}
|
|
101
133
|
/**
|
|
@@ -108,6 +140,7 @@ export declare function testDevAndDeploy(name: string, testFn: (context: {
|
|
|
108
140
|
browser: Browser;
|
|
109
141
|
page: Page;
|
|
110
142
|
url: string;
|
|
143
|
+
projectDir: string;
|
|
111
144
|
}) => Promise<void>): void;
|
|
112
145
|
export declare namespace testDevAndDeploy {
|
|
113
146
|
var skip: (name: string, testFn?: any) => void;
|