rwsdk 1.0.0-alpha.8 → 1.0.0-beta.0
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.mjs +1 -2
- package/dist/lib/e2e/browser.d.mts +1 -1
- package/dist/lib/e2e/browser.mjs +21 -5
- package/dist/lib/e2e/dev.mjs +62 -52
- package/dist/lib/e2e/environment.d.mts +2 -6
- package/dist/lib/e2e/environment.mjs +167 -158
- package/dist/lib/e2e/index.d.mts +5 -4
- package/dist/lib/e2e/index.mjs +5 -4
- package/dist/lib/e2e/poll.d.mts +8 -0
- package/dist/lib/e2e/poll.mjs +31 -0
- package/dist/lib/e2e/release.d.mts +1 -1
- package/dist/lib/e2e/release.mjs +56 -34
- package/dist/lib/e2e/retry.d.mts +4 -0
- package/dist/lib/e2e/retry.mjs +16 -0
- package/dist/lib/e2e/setup.d.mts +2 -0
- package/dist/lib/e2e/setup.mjs +1 -0
- package/dist/lib/e2e/tarball.d.mts +3 -3
- package/dist/lib/e2e/tarball.mjs +28 -118
- package/dist/lib/e2e/testHarness.d.mts +74 -40
- package/dist/lib/e2e/testHarness.mjs +320 -271
- package/dist/lib/e2e/types.d.mts +1 -0
- package/dist/lib/getShortName.mjs +1 -2
- package/dist/lib/getShortName.test.mjs +2 -2
- package/dist/lib/getSrcPaths.js +2 -2
- package/dist/lib/hasPkgScript.test.mjs +2 -2
- package/dist/lib/jsonUtils.test.mjs +2 -2
- package/dist/lib/normalizeModulePath.test.mjs +2 -2
- package/dist/lib/setupEnvFiles.mjs +2 -2
- package/dist/lib/smokeTests/artifacts.mjs +2 -2
- package/dist/lib/smokeTests/browser.d.mts +1 -1
- package/dist/lib/smokeTests/browser.mjs +6 -7
- package/dist/lib/smokeTests/cleanup.mjs +6 -9
- package/dist/lib/smokeTests/codeUpdates.mjs +5 -5
- package/dist/lib/smokeTests/development.mjs +2 -2
- package/dist/lib/smokeTests/environment.d.mts +2 -3
- package/dist/lib/smokeTests/environment.mjs +17 -3
- package/dist/lib/smokeTests/release.d.mts +2 -2
- package/dist/lib/smokeTests/release.mjs +3 -3
- package/dist/lib/smokeTests/reporting.mjs +2 -2
- package/dist/lib/smokeTests/runSmokeTests.mjs +4 -4
- package/dist/lib/smokeTests/utils.mjs +3 -3
- package/dist/lib/testUtils/stubEnvVars.mjs +1 -1
- package/dist/llms/rules/middleware.d.ts +1 -1
- package/dist/llms/rules/middleware.js +4 -4
- package/dist/runtime/client/client.d.ts +2 -2
- package/dist/runtime/client/client.js +2 -2
- package/dist/runtime/client/navigation.test.js +1 -1
- package/dist/runtime/client/types.d.ts +1 -1
- package/dist/runtime/entries/client.d.ts +2 -2
- package/dist/runtime/entries/client.js +2 -2
- package/dist/runtime/entries/router.d.ts +1 -1
- package/dist/runtime/entries/router.js +1 -1
- package/dist/runtime/entries/worker.d.ts +5 -6
- package/dist/runtime/entries/worker.js +5 -6
- package/dist/runtime/imports/worker.js +1 -1
- package/dist/runtime/lib/auth/session.d.ts +2 -2
- package/dist/runtime/lib/auth/session.js +5 -5
- package/dist/runtime/lib/db/DOWorkerDialect.d.ts +1 -1
- package/dist/runtime/lib/db/DOWorkerDialect.js +1 -1
- package/dist/runtime/lib/db/SqliteDurableObject.js +2 -2
- package/dist/runtime/lib/db/index.d.ts +2 -2
- package/dist/runtime/lib/db/index.js +2 -2
- package/dist/runtime/lib/db/migrations.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/builders/alterTable.d.ts +3 -3
- package/dist/runtime/lib/db/typeInference/builders/columnDefinition.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/builders/createTable.d.ts +2 -2
- package/dist/runtime/lib/db/typeInference/builders/createView.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/builders/dropTable.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/builders/dropView.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/builders/schema.d.ts +3 -3
- package/dist/runtime/lib/db/typeInference/database.d.ts +2 -2
- package/dist/runtime/lib/memoizeOnId.test.js +1 -1
- package/dist/runtime/lib/realtime/client.js +2 -2
- package/dist/runtime/lib/realtime/durableObject.js +1 -1
- package/dist/runtime/lib/realtime/protocol.test.js +1 -1
- package/dist/runtime/lib/realtime/shared.test.js +1 -1
- package/dist/runtime/lib/realtime/validateUpgradeRequest.test.js +1 -1
- package/dist/runtime/lib/realtime/worker.js +2 -2
- package/dist/runtime/lib/router.d.ts +1 -1
- package/dist/runtime/lib/router.test.js +2 -3
- package/dist/runtime/lib/rwContext.d.ts +1 -1
- package/dist/runtime/lib/stitchDocumentAndAppStreams.d.ts +18 -0
- package/dist/runtime/lib/stitchDocumentAndAppStreams.js +143 -0
- package/dist/runtime/lib/turnstile/useTurnstile.js +1 -1
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.js +1 -1
- package/dist/runtime/register/worker.d.ts +1 -1
- package/dist/runtime/register/worker.js +34 -22
- package/dist/runtime/render/assembleDocument.d.ts +1 -1
- package/dist/runtime/render/createThenableFromReadableStream.js +1 -1
- package/dist/runtime/render/preloads.d.ts +2 -2
- package/dist/runtime/render/renderDocumentHtmlStream.js +6 -6
- package/dist/runtime/render/renderHtmlStream.d.ts +1 -1
- package/dist/runtime/render/renderToRscStream.d.ts +4 -1
- package/dist/runtime/render/renderToRscStream.js +11 -1
- package/dist/runtime/render/renderToStream.d.ts +1 -1
- package/dist/runtime/render/renderToStream.js +2 -2
- package/dist/runtime/render/stylesheets.d.ts +1 -1
- package/dist/runtime/requestInfo/types.d.ts +0 -2
- package/dist/runtime/requestInfo/worker.d.ts +1 -1
- package/dist/runtime/requestInfo/worker.js +1 -9
- package/dist/runtime/script.js +1 -1
- package/dist/runtime/ssrBridge.d.ts +2 -2
- package/dist/runtime/ssrBridge.js +2 -2
- package/dist/runtime/worker.d.ts +1 -1
- package/dist/runtime/worker.js +3 -11
- package/dist/scripts/addon.d.mts +1 -0
- package/dist/scripts/addon.mjs +70 -0
- package/dist/scripts/debug-sync.mjs +106 -137
- package/dist/scripts/ensure-deploy-env.mjs +6 -6
- package/dist/scripts/migrate-new.mjs +3 -4
- package/dist/scripts/smoke-test.mjs +2 -2
- package/dist/scripts/worker-run.mjs +7 -9
- package/dist/vite/buildApp.d.mts +2 -1
- package/dist/vite/buildApp.mjs +10 -6
- package/dist/vite/checkIsUsingPrisma.test.mjs +1 -1
- package/dist/vite/configPlugin.mjs +35 -17
- package/dist/vite/createDirectiveLookupPlugin.mjs +1 -1
- package/dist/vite/createDirectiveLookupPlugin.test.mjs +2 -2
- package/dist/vite/createViteAwareResolver.d.mts +1 -2
- package/dist/vite/createViteAwareResolver.mjs +1 -1
- package/dist/vite/directiveModulesDevPlugin.d.mts +2 -1
- package/dist/vite/directiveModulesDevPlugin.mjs +6 -5
- package/dist/vite/directiveModulesDevPlugin.test.mjs +2 -2
- package/dist/vite/directivesPlugin.mjs +3 -3
- package/dist/vite/directivesPlugin.test.mjs +1 -1
- package/dist/vite/ensureAliasArray.test.mjs +1 -1
- package/dist/vite/findSpecifiers.mjs +1 -1
- package/dist/vite/findSpecifiers.test.mjs +2 -2
- package/dist/vite/findSsrSpecifiers.mjs +1 -1
- package/dist/vite/findSsrSpecifiers.test.mjs +1 -1
- package/dist/vite/getViteEsbuild.mjs +1 -1
- package/dist/vite/hasDirective.d.mts +6 -3
- package/dist/vite/hasDirective.mjs +43 -27
- package/dist/vite/hasDirective.test.mjs +73 -75
- package/dist/vite/index.d.mts +1 -1
- package/dist/vite/invalidateCacheIfPrismaClientChanged.mjs +2 -2
- package/dist/vite/isJsFile.test.mjs +1 -1
- package/dist/vite/{reactConditionsResolverPlugin.d.mts → knownDepsResolverPlugin.d.mts} +3 -3
- package/dist/vite/{reactConditionsResolverPlugin.mjs → knownDepsResolverPlugin.mjs} +29 -24
- package/dist/vite/linkerPlugin.mjs +2 -2
- package/dist/vite/linkerPlugin.test.mjs +1 -1
- package/dist/vite/miniflareHMRPlugin.mjs +5 -5
- package/dist/vite/miniflareHMRPlugin.test.mjs +1 -1
- package/dist/vite/prismaPlugin.mjs +1 -1
- package/dist/vite/redwoodPlugin.d.mts +2 -0
- package/dist/vite/redwoodPlugin.mjs +37 -17
- package/dist/vite/redwoodPlugin.test.mjs +2 -2
- package/dist/vite/resolveForcedPaths.d.mts +4 -0
- package/dist/vite/resolveForcedPaths.mjs +9 -0
- package/dist/vite/runDirectivesScan.d.mts +3 -1
- package/dist/vite/runDirectivesScan.mjs +60 -20
- package/dist/vite/runDirectivesScan.test.mjs +2 -2
- package/dist/vite/ssrBridgePlugin.mjs +10 -3
- package/dist/vite/transformClientComponents.mjs +8 -6
- package/dist/vite/transformClientComponents.test.mjs +117 -59
- package/dist/vite/transformJsxScriptTagsPlugin.mjs +1 -1
- package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +2 -2
- package/dist/vite/transformServerFunctions.d.mts +1 -1
- package/dist/vite/transformServerFunctions.mjs +5 -5
- package/dist/vite/transformServerFunctions.test.mjs +3 -3
- package/package.json +60 -50
- package/dist/runtime/imports/resolveSSRValue.d.ts +0 -1
- package/dist/runtime/imports/resolveSSRValue.js +0 -8
- package/dist/runtime/lib/injectHtmlAtMarker.d.ts +0 -11
- package/dist/runtime/lib/injectHtmlAtMarker.js +0 -90
package/dist/lib/constants.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import path from "node:path";
|
|
1
|
+
import path, { resolve } from "node:path";
|
|
3
2
|
const __dirname = new URL(".", import.meta.url).pathname;
|
|
4
3
|
export const ROOT_DIR = resolve(__dirname, "..", "..");
|
|
5
4
|
export const SRC_DIR = resolve(ROOT_DIR, "src");
|
package/dist/lib/e2e/browser.mjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import { computeExecutablePath, detectBrowserPlatform, install, Browser as PuppeteerBrowser, resolveBuildId, } from "@puppeteer/browsers";
|
|
2
|
+
import debug from "debug";
|
|
3
|
+
import { mkdirp, pathExists } from "fs-extra";
|
|
1
4
|
import * as os from "os";
|
|
2
5
|
import { join } from "path";
|
|
3
|
-
import { pathExists } from "fs-extra";
|
|
4
|
-
import debug from "debug";
|
|
5
|
-
import { mkdirp } from "fs-extra";
|
|
6
|
-
import { install, resolveBuildId, computeExecutablePath, detectBrowserPlatform, Browser as PuppeteerBrowser, } from "@puppeteer/browsers";
|
|
7
6
|
import puppeteer from "puppeteer-core";
|
|
8
7
|
const log = debug("rwsdk:e2e:browser");
|
|
9
8
|
/**
|
|
@@ -80,7 +79,24 @@ export async function getBrowserPath(testOptions) {
|
|
|
80
79
|
// Add better error handling for the install step
|
|
81
80
|
try {
|
|
82
81
|
console.log("Downloading Chrome (this may take a few minutes)...");
|
|
83
|
-
|
|
82
|
+
const attempts = 10;
|
|
83
|
+
let installError;
|
|
84
|
+
for (let i = 0; i < attempts; i++) {
|
|
85
|
+
try {
|
|
86
|
+
await install(installOptions);
|
|
87
|
+
installError = null; // Reset error on success
|
|
88
|
+
break; // Exit loop on success
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
installError = e;
|
|
92
|
+
console.log(`Attempt ${i + 1}/${attempts} failed. Retrying in 1 second...`);
|
|
93
|
+
// Wait for 1 second before retrying
|
|
94
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (installError) {
|
|
98
|
+
throw installError;
|
|
99
|
+
}
|
|
84
100
|
console.log("✅ Chrome installation completed successfully");
|
|
85
101
|
// Now compute the path for the installed browser
|
|
86
102
|
const path = computeExecutablePath(installOptions);
|
package/dist/lib/e2e/dev.mjs
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { setTimeout } from "node:timers/promises";
|
|
2
1
|
import debug from "debug";
|
|
2
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
3
3
|
import { $ } from "../../lib/$.mjs";
|
|
4
|
+
import { poll } from "./poll.mjs";
|
|
5
|
+
const DEV_SERVER_CHECK_TIMEOUT = process.env.RWSDK_DEV_SERVER_CHECK_TIMEOUT
|
|
6
|
+
? parseInt(process.env.RWSDK_DEV_SERVER_CHECK_TIMEOUT, 10)
|
|
7
|
+
: 5 * 60 * 1000;
|
|
4
8
|
const log = debug("rwsdk:e2e:dev");
|
|
5
9
|
/**
|
|
6
10
|
* Run the local development server and return the URL
|
|
@@ -12,59 +16,47 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
12
16
|
let isErrorExpected = false;
|
|
13
17
|
const stopDev = async () => {
|
|
14
18
|
isErrorExpected = true;
|
|
15
|
-
if (!devProcess) {
|
|
16
|
-
log("No dev process to stop");
|
|
19
|
+
if (!devProcess || !devProcess.pid) {
|
|
20
|
+
log("No dev process to stop or PID is missing");
|
|
17
21
|
return;
|
|
18
22
|
}
|
|
19
23
|
console.log("Stopping development server...");
|
|
20
24
|
try {
|
|
21
|
-
// Send a regular termination signal first
|
|
22
|
-
|
|
23
|
-
// Wait for the process to terminate with a timeout
|
|
24
|
-
const terminationTimeout = 5000; // 5 seconds timeout
|
|
25
|
-
const terminationPromise = Promise.race([
|
|
26
|
-
// Wait for natural process termination
|
|
27
|
-
(async () => {
|
|
28
|
-
try {
|
|
29
|
-
await devProcess;
|
|
30
|
-
log("Dev server process was terminated normally");
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
catch (e) {
|
|
34
|
-
// Expected error when the process is killed
|
|
35
|
-
log("Dev server process was terminated");
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
})(),
|
|
39
|
-
// Or timeout
|
|
40
|
-
(async () => {
|
|
41
|
-
await setTimeout(terminationTimeout);
|
|
42
|
-
return false;
|
|
43
|
-
})(),
|
|
44
|
-
]);
|
|
45
|
-
// Check if process terminated within timeout
|
|
46
|
-
const terminated = await terminationPromise;
|
|
47
|
-
// If not terminated within timeout, force kill
|
|
48
|
-
if (!terminated) {
|
|
49
|
-
log("Dev server process did not terminate within timeout, force killing with SIGKILL");
|
|
50
|
-
console.log("⚠️ Development server not responding after 5 seconds timeout, force killing...");
|
|
51
|
-
// Try to kill with SIGKILL if the process still has a pid
|
|
52
|
-
if (devProcess.pid) {
|
|
53
|
-
try {
|
|
54
|
-
// Use process.kill with SIGKILL for a stronger termination
|
|
55
|
-
process.kill(devProcess.pid, "SIGKILL");
|
|
56
|
-
log("Sent SIGKILL to process %d", devProcess.pid);
|
|
57
|
-
}
|
|
58
|
-
catch (killError) {
|
|
59
|
-
log("Error sending SIGKILL to process: %O", killError);
|
|
60
|
-
// Non-fatal, as the process might already be gone
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
25
|
+
// Send a regular termination signal to the entire process group first
|
|
26
|
+
process.kill(-devProcess.pid, "SIGTERM");
|
|
64
27
|
}
|
|
65
28
|
catch (e) {
|
|
66
|
-
|
|
67
|
-
|
|
29
|
+
log("Could not send SIGTERM to dev server process group: %O", e);
|
|
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;
|
|
49
|
+
}
|
|
50
|
+
// If not terminated within timeout, force kill the entire process group
|
|
51
|
+
if (isAlive) {
|
|
52
|
+
log("Dev server process did not terminate within timeout, force killing with SIGKILL");
|
|
53
|
+
console.log("⚠️ Development server not responding after 5 seconds timeout, force killing...");
|
|
54
|
+
try {
|
|
55
|
+
process.kill(-devProcess.pid, "SIGKILL");
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
log("Could not send SIGKILL to dev server process group: %O", e);
|
|
59
|
+
}
|
|
68
60
|
}
|
|
69
61
|
console.log("Development server stopped");
|
|
70
62
|
};
|
|
@@ -96,7 +88,7 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
96
88
|
// Use the provided cwd if available
|
|
97
89
|
devProcess = $({
|
|
98
90
|
all: true,
|
|
99
|
-
detached:
|
|
91
|
+
detached: true, // Run in a new process group so we can kill the entire group
|
|
100
92
|
cleanup: false, // Don't auto-kill on exit
|
|
101
93
|
cwd: cwd || process.cwd(), // Use provided directory or current directory
|
|
102
94
|
env, // Pass the updated environment variables
|
|
@@ -104,8 +96,10 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
104
96
|
}) `${pm} run dev`;
|
|
105
97
|
devProcess.catch((error) => {
|
|
106
98
|
if (!isErrorExpected) {
|
|
107
|
-
//
|
|
108
|
-
|
|
99
|
+
// Don't re-throw. The error will be handled gracefully by the polling
|
|
100
|
+
// logic in `waitForUrl`, which will detect that the process has exited.
|
|
101
|
+
// Re-throwing here would cause an unhandled promise rejection.
|
|
102
|
+
log("Dev server process exited unexpectedly:", error.shortMessage);
|
|
109
103
|
}
|
|
110
104
|
});
|
|
111
105
|
log("Development server process spawned in directory: %s", cwd || process.cwd());
|
|
@@ -211,7 +205,7 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
211
205
|
log("ERROR: Development server process exited with code %d. Final output: %s", devProcess.exitCode, allOutput);
|
|
212
206
|
throw new Error(`Development server process exited with code ${devProcess.exitCode}`);
|
|
213
207
|
}
|
|
214
|
-
await
|
|
208
|
+
await sleep(500); // Check every 500ms
|
|
215
209
|
}
|
|
216
210
|
log("ERROR: Timed out waiting for dev server URL. Final accumulated output: %s", allOutput);
|
|
217
211
|
throw new Error("Timed out waiting for dev server URL");
|
|
@@ -219,6 +213,22 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
219
213
|
// Wait for the URL
|
|
220
214
|
const serverUrl = await waitForUrl();
|
|
221
215
|
console.log(`✅ Development server started at ${serverUrl}`);
|
|
216
|
+
// Poll the URL to ensure it's live before proceeding
|
|
217
|
+
await poll(async () => {
|
|
218
|
+
try {
|
|
219
|
+
const response = await fetch(serverUrl, {
|
|
220
|
+
signal: AbortSignal.timeout(1000),
|
|
221
|
+
});
|
|
222
|
+
// We consider any response (even 4xx or 5xx) as success,
|
|
223
|
+
// as it means the worker is routable.
|
|
224
|
+
return response.status > 0;
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
}, {
|
|
230
|
+
timeout: DEV_SERVER_CHECK_TIMEOUT,
|
|
231
|
+
});
|
|
222
232
|
return { url: serverUrl, stopDev };
|
|
223
233
|
}
|
|
224
234
|
catch (error) {
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import tmp from "tmp-promise";
|
|
2
|
-
import {
|
|
3
|
-
/**
|
|
4
|
-
* Sets up the test environment, preparing any resources needed for testing
|
|
5
|
-
*/
|
|
6
|
-
export declare function setupTestEnvironment(options?: SmokeTestOptions): Promise<TestResources>;
|
|
2
|
+
import { PackageManager } from "./types.mjs";
|
|
7
3
|
/**
|
|
8
4
|
* Copy project to a temporary directory with a unique name
|
|
9
5
|
*/
|
|
10
|
-
export declare function copyProjectToTempDir(projectDir: string,
|
|
6
|
+
export declare function copyProjectToTempDir(projectDir: string, resourceUniqueKey: string, packageManager?: PackageManager, monorepoRoot?: string): Promise<{
|
|
11
7
|
tempDir: tmp.DirectoryResult;
|
|
12
8
|
targetDir: string;
|
|
13
9
|
workerName: string;
|
|
@@ -1,184 +1,190 @@
|
|
|
1
|
-
import { join } from "path";
|
|
2
1
|
import debug from "debug";
|
|
3
|
-
import {
|
|
4
|
-
import * as fs from "fs/promises";
|
|
5
|
-
import tmp from "tmp-promise";
|
|
2
|
+
import { copy, pathExists } from "fs-extra";
|
|
6
3
|
import ignore from "ignore";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import os from "os";
|
|
7
|
+
import { basename, join, relative, resolve } from "path";
|
|
8
|
+
import tmp from "tmp-promise";
|
|
9
9
|
import { $ } from "../../lib/$.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { ROOT_DIR } from "../constants.mjs";
|
|
11
|
+
import { retry } from "./retry.mjs";
|
|
12
12
|
const log = debug("rwsdk:e2e:environment");
|
|
13
|
+
const createSdkTarball = async () => {
|
|
14
|
+
const packResult = await $({ cwd: ROOT_DIR, stdio: "pipe" }) `npm pack`;
|
|
15
|
+
const tarballName = packResult.stdout?.trim();
|
|
16
|
+
const tarballPath = path.join(ROOT_DIR, tarballName);
|
|
17
|
+
log(`📦 Created tarball: ${tarballPath}`);
|
|
18
|
+
const cleanupTarball = async () => {
|
|
19
|
+
if (fs.existsSync(tarballPath)) {
|
|
20
|
+
log(`🧹 Cleaning up tarball: ${tarballPath}`);
|
|
21
|
+
await fs.promises.rm(tarballPath, { force: true });
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
return { tarballPath, cleanupTarball };
|
|
25
|
+
};
|
|
26
|
+
const setTarballDependency = async (targetDir, tarballName) => {
|
|
27
|
+
const filePath = join(targetDir, "package.json");
|
|
28
|
+
const packageJson = await fs.promises.readFile(filePath, "utf-8");
|
|
29
|
+
const packageJsonContent = JSON.parse(packageJson);
|
|
30
|
+
packageJsonContent.dependencies.rwsdk = `file:${tarballName}`;
|
|
31
|
+
await fs.promises.writeFile(filePath, JSON.stringify(packageJsonContent, null, 2));
|
|
32
|
+
};
|
|
13
33
|
/**
|
|
14
|
-
*
|
|
34
|
+
* Copy project to a temporary directory with a unique name
|
|
15
35
|
*/
|
|
16
|
-
export async function
|
|
17
|
-
|
|
18
|
-
// Generate a resource unique key for this test run right at the start
|
|
19
|
-
const uniqueNameSuffix = uniqueNamesGenerator({
|
|
20
|
-
dictionaries: [adjectives, animals],
|
|
21
|
-
separator: "-",
|
|
22
|
-
length: 2,
|
|
23
|
-
style: "lowerCase",
|
|
24
|
-
});
|
|
25
|
-
// Create a short unique hash based on the timestamp
|
|
26
|
-
const hash = createHash("md5")
|
|
27
|
-
.update(Date.now().toString())
|
|
28
|
-
.digest("hex")
|
|
29
|
-
.substring(0, 8);
|
|
30
|
-
// Create a resource unique key even if we're not copying a project
|
|
31
|
-
const resourceUniqueKey = `${uniqueNameSuffix}-${hash}`;
|
|
32
|
-
const resources = {
|
|
33
|
-
tempDirCleanup: undefined,
|
|
34
|
-
workerName: undefined,
|
|
35
|
-
originalCwd: process.cwd(),
|
|
36
|
-
targetDir: undefined,
|
|
37
|
-
workerCreatedDuringTest: false,
|
|
38
|
-
stopDev: undefined,
|
|
39
|
-
resourceUniqueKey, // Set at initialization
|
|
40
|
-
};
|
|
41
|
-
log("Current working directory: %s", resources.originalCwd);
|
|
36
|
+
export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager, monorepoRoot) {
|
|
37
|
+
const { tarballPath, cleanupTarball } = await createSdkTarball();
|
|
42
38
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
log("Creating temporary directory for project");
|
|
40
|
+
// Create a temporary directory
|
|
41
|
+
const tempDir = await tmp.dir({ unsafeCleanup: true });
|
|
42
|
+
// Determine the source directory to copy from
|
|
43
|
+
const sourceDir = monorepoRoot || projectDir;
|
|
44
|
+
// Create unique project directory name
|
|
45
|
+
const originalDirName = basename(sourceDir);
|
|
46
|
+
const workerName = `${originalDirName}-test-${resourceUniqueKey}`;
|
|
47
|
+
const tempCopyRoot = resolve(tempDir.path, workerName);
|
|
48
|
+
// If it's a monorepo, the targetDir for commands is a subdirectory
|
|
49
|
+
const targetDir = monorepoRoot
|
|
50
|
+
? resolve(tempCopyRoot, relative(monorepoRoot, projectDir))
|
|
51
|
+
: tempCopyRoot;
|
|
52
|
+
console.log(`Copying project from ${sourceDir} to ${tempCopyRoot}`);
|
|
53
|
+
// Read project's .gitignore if it exists
|
|
54
|
+
let ig = ignore();
|
|
55
|
+
const gitignorePath = join(sourceDir, ".gitignore");
|
|
56
|
+
if (await pathExists(gitignorePath)) {
|
|
57
|
+
log("Found .gitignore file at %s", gitignorePath);
|
|
58
|
+
const gitignoreContent = await fs.promises.readFile(gitignorePath, "utf-8");
|
|
59
|
+
ig = ig.add(gitignoreContent);
|
|
54
60
|
}
|
|
55
61
|
else {
|
|
56
|
-
log("No
|
|
57
|
-
//
|
|
58
|
-
|
|
62
|
+
log("No .gitignore found, using default ignore patterns");
|
|
63
|
+
// Add default ignores if no .gitignore exists
|
|
64
|
+
ig = ig.add([
|
|
65
|
+
"node_modules",
|
|
66
|
+
".git",
|
|
67
|
+
"dist",
|
|
68
|
+
"build",
|
|
69
|
+
".DS_Store",
|
|
70
|
+
"coverage",
|
|
71
|
+
".cache",
|
|
72
|
+
".wrangler",
|
|
73
|
+
".env",
|
|
74
|
+
].join("\n"));
|
|
59
75
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// Add default ignores if no .gitignore exists
|
|
90
|
-
ig = ig.add([
|
|
91
|
-
"node_modules",
|
|
92
|
-
".git",
|
|
93
|
-
"dist",
|
|
94
|
-
"build",
|
|
95
|
-
".DS_Store",
|
|
96
|
-
"coverage",
|
|
97
|
-
".cache",
|
|
98
|
-
".wrangler",
|
|
99
|
-
".env",
|
|
100
|
-
].join("\n"));
|
|
101
|
-
}
|
|
102
|
-
// Copy the project directory, respecting .gitignore
|
|
103
|
-
log("Starting copy process with ignored patterns");
|
|
104
|
-
await copy(projectDir, targetDir, {
|
|
105
|
-
filter: (src) => {
|
|
106
|
-
// Get path relative to project directory
|
|
107
|
-
const relativePath = relative(projectDir, src);
|
|
108
|
-
if (!relativePath)
|
|
109
|
-
return true; // Include the root directory
|
|
110
|
-
// Check against ignore patterns
|
|
111
|
-
const result = !ig.ignores(relativePath);
|
|
112
|
-
return result;
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
log("Project copy completed successfully");
|
|
116
|
-
// For yarn, create .yarnrc.yml to disable PnP and use node_modules
|
|
117
|
-
if (packageManager === "yarn" || packageManager === "yarn-classic") {
|
|
118
|
-
const yarnrcPath = join(targetDir, ".yarnrc.yml");
|
|
119
|
-
await fs.writeFile(yarnrcPath, "nodeLinker: node-modules\n");
|
|
120
|
-
log("Created .yarnrc.yml to disable PnP for yarn");
|
|
121
|
-
}
|
|
122
|
-
// Replace workspace:* dependencies with a placeholder before installing
|
|
123
|
-
await replaceWorkspaceDependencies(targetDir);
|
|
124
|
-
// Install dependencies in the target directory
|
|
125
|
-
await installDependencies(targetDir, packageManager);
|
|
126
|
-
// Sync SDK to the temp dir if requested
|
|
127
|
-
if (sync) {
|
|
128
|
-
console.log(`🔄 Syncing SDK to ${targetDir} after installing dependencies...`);
|
|
129
|
-
await debugSync({ targetDir });
|
|
130
|
-
}
|
|
131
|
-
return { tempDir, targetDir, workerName };
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Replace workspace:* dependencies with a placeholder version to allow installation
|
|
135
|
-
*/
|
|
136
|
-
async function replaceWorkspaceDependencies(targetDir) {
|
|
137
|
-
const packageJsonPath = join(targetDir, "package.json");
|
|
138
|
-
try {
|
|
139
|
-
const packageJsonContent = await fs.readFile(packageJsonPath, "utf-8");
|
|
140
|
-
const packageJson = JSON.parse(packageJsonContent);
|
|
141
|
-
let modified = false;
|
|
142
|
-
// Replace workspace:* dependencies with a placeholder version
|
|
143
|
-
if (packageJson.dependencies) {
|
|
144
|
-
for (const [name, version] of Object.entries(packageJson.dependencies)) {
|
|
145
|
-
if (version === "workspace:*") {
|
|
146
|
-
packageJson.dependencies[name] = "0.0.80"; // Use latest published version as placeholder
|
|
147
|
-
modified = true;
|
|
148
|
-
log(`Replaced workspace dependency ${name} with placeholder version`);
|
|
76
|
+
// Copy the project directory, respecting .gitignore
|
|
77
|
+
log("Starting copy process with ignored patterns");
|
|
78
|
+
await copy(sourceDir, tempCopyRoot, {
|
|
79
|
+
filter: (src) => {
|
|
80
|
+
// Get path relative to project directory
|
|
81
|
+
const relativePath = relative(sourceDir, src);
|
|
82
|
+
if (!relativePath)
|
|
83
|
+
return true; // Include the root directory
|
|
84
|
+
// Check against ignore patterns
|
|
85
|
+
const result = !ig.ignores(relativePath);
|
|
86
|
+
return result;
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
log("Project copy completed successfully");
|
|
90
|
+
// Copy the SDK tarball into the target directory
|
|
91
|
+
const tarballFilename = basename(tarballPath);
|
|
92
|
+
const tempTarballPath = join(targetDir, tarballFilename);
|
|
93
|
+
await fs.promises.copyFile(tarballPath, tempTarballPath);
|
|
94
|
+
if (monorepoRoot) {
|
|
95
|
+
log("⚙️ Configuring monorepo workspace...");
|
|
96
|
+
const rwsdkWsPath = join(tempCopyRoot, "rwsdk-workspace.json");
|
|
97
|
+
if (await pathExists(rwsdkWsPath)) {
|
|
98
|
+
const rwsdkWs = JSON.parse(await fs.promises.readFile(rwsdkWsPath, "utf-8"));
|
|
99
|
+
const workspaces = rwsdkWs.workspaces;
|
|
100
|
+
if (packageManager === "pnpm") {
|
|
101
|
+
const pnpmWsPath = join(tempCopyRoot, "pnpm-workspace.yaml");
|
|
102
|
+
const pnpmWsConfig = `packages:\n${workspaces.map((w) => ` - '${w}'`).join("\n")}\n`;
|
|
103
|
+
await fs.promises.writeFile(pnpmWsPath, pnpmWsConfig);
|
|
104
|
+
log("Created pnpm-workspace.yaml");
|
|
149
105
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
log(`Replaced workspace devDependency ${name} with placeholder version`);
|
|
106
|
+
else {
|
|
107
|
+
// For npm and yarn, add a workspaces property to package.json
|
|
108
|
+
const pkgJsonPath = join(tempCopyRoot, "package.json");
|
|
109
|
+
const pkgJson = JSON.parse(await fs.promises.readFile(pkgJsonPath, "utf-8"));
|
|
110
|
+
pkgJson.workspaces = workspaces;
|
|
111
|
+
await fs.promises.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
112
|
+
log("Added workspaces to package.json");
|
|
158
113
|
}
|
|
159
114
|
}
|
|
160
115
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
116
|
+
// Configure temp project to not use frozen lockfile
|
|
117
|
+
log("⚙️ Configuring temp project to not use frozen lockfile...");
|
|
118
|
+
const npmrcPath = join(targetDir, ".npmrc");
|
|
119
|
+
await fs.promises.writeFile(npmrcPath, "frozen-lockfile=false\n");
|
|
120
|
+
// For yarn, create .yarnrc.yml to disable PnP and allow lockfile changes
|
|
121
|
+
if (packageManager === "yarn") {
|
|
122
|
+
const yarnrcPath = join(targetDir, ".yarnrc.yml");
|
|
123
|
+
const yarnCacheDir = path.join(os.tmpdir(), "yarn-cache");
|
|
124
|
+
await fs.promises.mkdir(yarnCacheDir, { recursive: true });
|
|
125
|
+
const yarnConfig = [
|
|
126
|
+
// todo(justinvdm, 23-09-23): Support yarn pnpm
|
|
127
|
+
"nodeLinker: node-modules",
|
|
128
|
+
"enableImmutableInstalls: false",
|
|
129
|
+
`cacheFolder: "${yarnCacheDir}"`,
|
|
130
|
+
].join("\n");
|
|
131
|
+
await fs.promises.writeFile(yarnrcPath, yarnConfig);
|
|
132
|
+
log("Created .yarnrc.yml to allow lockfile changes for yarn");
|
|
164
133
|
}
|
|
134
|
+
await setTarballDependency(targetDir, tarballFilename);
|
|
135
|
+
// Install dependencies in the target directory
|
|
136
|
+
const installDir = monorepoRoot ? tempCopyRoot : targetDir;
|
|
137
|
+
await retry(() => installDependencies(installDir, packageManager), {
|
|
138
|
+
retries: 3,
|
|
139
|
+
delay: 1000,
|
|
140
|
+
});
|
|
141
|
+
// Return the environment details
|
|
142
|
+
return { tempDir, targetDir, workerName };
|
|
165
143
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
throw new Error(`Failed to replace workspace dependencies: ${error}`);
|
|
144
|
+
finally {
|
|
145
|
+
await cleanupTarball();
|
|
169
146
|
}
|
|
170
147
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Install project dependencies using pnpm
|
|
173
|
-
*/
|
|
174
148
|
async function installDependencies(targetDir, packageManager = "pnpm") {
|
|
175
149
|
console.log(`📦 Installing project dependencies in ${targetDir} using ${packageManager}...`);
|
|
176
150
|
try {
|
|
151
|
+
// Clean up any pre-existing node_modules and lockfiles
|
|
152
|
+
log("Cleaning up pre-existing node_modules and lockfiles...");
|
|
153
|
+
await Promise.all([
|
|
154
|
+
fs.promises.rm(join(targetDir, "node_modules"), {
|
|
155
|
+
recursive: true,
|
|
156
|
+
force: true,
|
|
157
|
+
}),
|
|
158
|
+
fs.promises.rm(join(targetDir, "pnpm-lock.yaml"), { force: true }),
|
|
159
|
+
fs.promises.rm(join(targetDir, "yarn.lock"), { force: true }),
|
|
160
|
+
fs.promises.rm(join(targetDir, "package-lock.json"), { force: true }),
|
|
161
|
+
]);
|
|
162
|
+
log("Cleanup complete.");
|
|
163
|
+
if (packageManager.startsWith("yarn")) {
|
|
164
|
+
log(`Enabling corepack...`);
|
|
165
|
+
await $("corepack", ["enable"], { cwd: targetDir, stdio: "pipe" });
|
|
166
|
+
if (packageManager === "yarn") {
|
|
167
|
+
log(`Preparing yarn@stable with corepack...`);
|
|
168
|
+
await $("corepack", ["prepare", "yarn@stable", "--activate"], {
|
|
169
|
+
cwd: targetDir,
|
|
170
|
+
stdio: "pipe",
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
else if (packageManager === "yarn-classic") {
|
|
174
|
+
log(`Preparing yarn@1.22.19 with corepack...`);
|
|
175
|
+
await $("corepack", ["prepare", "yarn@1.22.19", "--activate"], {
|
|
176
|
+
cwd: targetDir,
|
|
177
|
+
stdio: "pipe",
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const npmCacheDir = path.join(os.tmpdir(), "npm-cache");
|
|
182
|
+
await fs.promises.mkdir(npmCacheDir, { recursive: true });
|
|
177
183
|
const installCommand = {
|
|
178
184
|
pnpm: ["pnpm", "install"],
|
|
179
|
-
npm: ["npm", "install"],
|
|
180
|
-
yarn: ["yarn", "install"
|
|
181
|
-
"yarn-classic": ["yarn"
|
|
185
|
+
npm: ["npm", "install", "--cache", npmCacheDir],
|
|
186
|
+
yarn: ["yarn", "install"],
|
|
187
|
+
"yarn-classic": ["yarn"],
|
|
182
188
|
}[packageManager];
|
|
183
189
|
// Run install command in the target directory
|
|
184
190
|
log(`Running ${installCommand.join(" ")}`);
|
|
@@ -186,6 +192,9 @@ async function installDependencies(targetDir, packageManager = "pnpm") {
|
|
|
186
192
|
const result = await $(command, args, {
|
|
187
193
|
cwd: targetDir,
|
|
188
194
|
stdio: "pipe", // Capture output
|
|
195
|
+
env: {
|
|
196
|
+
YARN_ENABLE_HARDENED_MODE: "0",
|
|
197
|
+
},
|
|
189
198
|
});
|
|
190
199
|
console.log("✅ Dependencies installed successfully");
|
|
191
200
|
// Log installation details at debug level
|
package/dist/lib/e2e/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export * from "./testHarness.mjs";
|
|
2
|
-
export * from "./tarball.mjs";
|
|
3
|
-
export * from "./environment.mjs";
|
|
4
|
-
export * from "./dev.mjs";
|
|
5
1
|
export * from "./browser.mjs";
|
|
2
|
+
export * from "./dev.mjs";
|
|
3
|
+
export * from "./environment.mjs";
|
|
4
|
+
export * from "./poll.mjs";
|
|
6
5
|
export * from "./release.mjs";
|
|
6
|
+
export * from "./tarball.mjs";
|
|
7
|
+
export * from "./testHarness.mjs";
|
|
7
8
|
export * from "./types.mjs";
|
package/dist/lib/e2e/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export * from "./testHarness.mjs";
|
|
2
|
-
export * from "./tarball.mjs";
|
|
3
|
-
export * from "./environment.mjs";
|
|
4
|
-
export * from "./dev.mjs";
|
|
5
1
|
export * from "./browser.mjs";
|
|
2
|
+
export * from "./dev.mjs";
|
|
3
|
+
export * from "./environment.mjs";
|
|
4
|
+
export * from "./poll.mjs";
|
|
6
5
|
export * from "./release.mjs";
|
|
6
|
+
export * from "./tarball.mjs";
|
|
7
|
+
export * from "./testHarness.mjs";
|
|
7
8
|
export * from "./types.mjs";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface PollOptions {
|
|
2
|
+
timeout: number;
|
|
3
|
+
interval: number;
|
|
4
|
+
minTries: number;
|
|
5
|
+
onRetry?: (error: unknown, tries: number) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function poll(fn: () => Promise<boolean>, options?: Partial<PollOptions>): Promise<void>;
|
|
8
|
+
export declare function pollValue<T>(fn: () => Promise<T>, options?: Partial<PollOptions>): Promise<T>;
|