rwsdk 1.0.0-alpha.2 → 1.0.0-alpha.20-test.20250929144616
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 +10 -0
- package/dist/lib/e2e/browser.mjs +123 -0
- package/dist/lib/e2e/dev.d.mts +8 -0
- package/dist/lib/e2e/dev.mjs +242 -0
- package/dist/lib/e2e/environment.d.mts +10 -0
- package/dist/lib/e2e/environment.mjs +210 -0
- package/dist/lib/e2e/index.d.mts +8 -0
- package/dist/lib/e2e/index.mjs +8 -0
- package/dist/lib/e2e/poll.d.mts +8 -0
- package/dist/lib/e2e/poll.mjs +31 -0
- package/dist/lib/e2e/release.d.mts +56 -0
- package/dist/lib/e2e/release.mjs +559 -0
- 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 +14 -0
- package/dist/lib/e2e/tarball.mjs +99 -0
- package/dist/lib/e2e/testHarness.d.mts +132 -0
- package/dist/lib/e2e/testHarness.mjs +436 -0
- package/dist/lib/e2e/types.d.mts +32 -0
- package/dist/lib/getShortName.mjs +6 -2
- package/dist/lib/getShortName.test.d.mts +1 -0
- package/dist/lib/getShortName.test.mjs +25 -0
- package/dist/lib/getSrcPaths.js +2 -2
- package/dist/lib/hasPkgScript.d.mts +4 -1
- package/dist/lib/hasPkgScript.mjs +9 -6
- package/dist/lib/hasPkgScript.test.d.mts +1 -0
- package/dist/lib/hasPkgScript.test.mjs +33 -0
- package/dist/lib/jsonUtils.mjs +3 -0
- package/dist/lib/jsonUtils.test.d.mts +1 -0
- package/dist/lib/jsonUtils.test.mjs +90 -0
- package/dist/lib/normalizeModulePath.d.mts +5 -0
- package/dist/lib/normalizeModulePath.mjs +1 -1
- package/dist/lib/normalizeModulePath.test.d.mts +1 -0
- package/dist/lib/{normalizeModulePath.test.js → normalizeModulePath.test.mjs} +21 -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 +8 -100
- package/dist/lib/smokeTests/cleanup.mjs +6 -9
- package/dist/lib/smokeTests/codeUpdates.mjs +5 -5
- package/dist/lib/smokeTests/development.mjs +3 -224
- package/dist/lib/smokeTests/environment.d.mts +3 -11
- package/dist/lib/smokeTests/environment.mjs +17 -151
- package/dist/lib/smokeTests/release.d.mts +2 -49
- package/dist/lib/smokeTests/release.mjs +4 -504
- 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.d.ts +1 -0
- package/dist/runtime/lib/memoizeOnId.test.js +49 -0
- 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.d.ts +1 -0
- package/dist/runtime/lib/realtime/protocol.test.js +107 -0
- package/dist/runtime/lib/realtime/shared.test.d.ts +1 -0
- package/dist/runtime/lib/realtime/shared.test.js +18 -0
- package/dist/runtime/lib/realtime/validateUpgradeRequest.test.d.ts +1 -0
- package/dist/runtime/lib/realtime/validateUpgradeRequest.test.js +66 -0
- package/dist/runtime/lib/realtime/worker.d.ts +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.js +40 -22
- package/dist/runtime/lib/router.test.js +591 -3
- package/dist/runtime/lib/rwContext.d.ts +22 -0
- package/dist/runtime/lib/rwContext.js +1 -0
- 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.d.ts +2 -1
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.js +6 -6
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.d.ts +1 -0
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.js +49 -0
- package/dist/runtime/register/worker.d.ts +1 -1
- package/dist/runtime/register/worker.js +34 -22
- package/dist/runtime/render/assembleDocument.d.ts +6 -0
- package/dist/runtime/render/assembleDocument.js +22 -0
- package/dist/runtime/render/createThenableFromReadableStream.d.ts +1 -0
- package/dist/runtime/render/createThenableFromReadableStream.js +9 -0
- package/dist/runtime/render/normalizeActionResult.d.ts +1 -0
- package/dist/runtime/render/normalizeActionResult.js +43 -0
- package/dist/runtime/render/preloads.d.ts +3 -3
- package/dist/runtime/render/preloads.js +2 -3
- package/dist/runtime/render/{renderRscThenableToHtmlStream.d.ts → renderDocumentHtmlStream.d.ts} +3 -3
- package/dist/runtime/render/renderDocumentHtmlStream.js +39 -0
- package/dist/runtime/render/renderHtmlStream.d.ts +7 -0
- package/dist/runtime/render/renderHtmlStream.js +31 -0
- package/dist/runtime/render/renderToRscStream.d.ts +5 -3
- package/dist/runtime/render/renderToRscStream.js +12 -41
- package/dist/runtime/render/renderToStream.d.ts +3 -2
- package/dist/runtime/render/renderToStream.js +17 -10
- package/dist/runtime/render/stylesheets.d.ts +2 -2
- package/dist/runtime/render/stylesheets.js +2 -3
- 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 +3 -2
- package/dist/runtime/ssrBridge.js +3 -2
- package/dist/runtime/worker.d.ts +2 -1
- package/dist/runtime/worker.js +13 -16
- package/dist/scripts/addon.d.mts +1 -0
- package/dist/scripts/addon.mjs +75 -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.d.mts +4 -0
- package/dist/vite/checkIsUsingPrisma.mjs +2 -2
- package/dist/vite/checkIsUsingPrisma.test.d.mts +1 -0
- package/dist/vite/checkIsUsingPrisma.test.mjs +30 -0
- package/dist/vite/configPlugin.mjs +55 -15
- package/dist/vite/createDirectiveLookupPlugin.d.mts +9 -0
- package/dist/vite/createDirectiveLookupPlugin.mjs +34 -30
- package/dist/vite/createDirectiveLookupPlugin.test.d.mts +1 -0
- package/dist/vite/createDirectiveLookupPlugin.test.mjs +40 -0
- package/dist/vite/createViteAwareResolver.d.mts +1 -2
- package/dist/vite/createViteAwareResolver.mjs +1 -1
- package/dist/vite/directiveModulesDevPlugin.d.mts +4 -1
- package/dist/vite/directiveModulesDevPlugin.mjs +9 -8
- package/dist/vite/directiveModulesDevPlugin.test.d.mts +1 -0
- package/dist/vite/directiveModulesDevPlugin.test.mjs +59 -0
- package/dist/vite/directivesPlugin.d.mts +1 -0
- package/dist/vite/directivesPlugin.mjs +4 -4
- package/dist/vite/directivesPlugin.test.d.mts +1 -0
- package/dist/vite/directivesPlugin.test.mjs +24 -0
- package/dist/vite/ensureAliasArray.test.d.mts +1 -0
- package/dist/vite/ensureAliasArray.test.mjs +71 -0
- package/dist/vite/findSpecifiers.mjs +3 -2
- package/dist/vite/findSpecifiers.test.d.mts +1 -0
- package/dist/vite/findSpecifiers.test.mjs +202 -0
- package/dist/vite/findSsrSpecifiers.mjs +1 -1
- package/dist/vite/findSsrSpecifiers.test.d.mts +1 -0
- package/dist/vite/findSsrSpecifiers.test.mjs +99 -0
- 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.d.mts +1 -0
- package/dist/vite/hasDirective.test.mjs +107 -0
- package/dist/vite/index.d.mts +1 -1
- package/dist/vite/invalidateCacheIfPrismaClientChanged.mjs +2 -2
- package/dist/vite/isJsFile.test.d.mts +1 -0
- package/dist/vite/isJsFile.test.mjs +38 -0
- package/dist/vite/{reactConditionsResolverPlugin.d.mts → knownDepsResolverPlugin.d.mts} +3 -3
- package/dist/vite/{reactConditionsResolverPlugin.mjs → knownDepsResolverPlugin.mjs} +29 -24
- package/dist/vite/linkerPlugin.d.mts +8 -0
- package/dist/vite/linkerPlugin.mjs +32 -24
- package/dist/vite/linkerPlugin.test.d.mts +1 -0
- package/dist/vite/linkerPlugin.test.mjs +41 -0
- package/dist/vite/miniflareHMRPlugin.d.mts +5 -0
- package/dist/vite/miniflareHMRPlugin.mjs +7 -7
- package/dist/vite/miniflareHMRPlugin.test.d.mts +1 -0
- package/dist/vite/miniflareHMRPlugin.test.mjs +42 -0
- package/dist/vite/prismaPlugin.mjs +1 -1
- package/dist/vite/redwoodPlugin.d.mts +9 -0
- package/dist/vite/redwoodPlugin.mjs +44 -20
- package/dist/vite/redwoodPlugin.test.d.mts +1 -0
- package/dist/vite/redwoodPlugin.test.mjs +34 -0
- package/dist/vite/resolveForcedPaths.d.mts +4 -0
- package/dist/vite/resolveForcedPaths.mjs +9 -0
- package/dist/vite/runDirectivesScan.d.mts +22 -1
- package/dist/vite/runDirectivesScan.mjs +109 -61
- package/dist/vite/runDirectivesScan.test.d.mts +1 -0
- package/dist/vite/runDirectivesScan.test.mjs +73 -0
- 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 +61 -47
- package/dist/runtime/imports/resolveSSRValue.d.ts +0 -1
- package/dist/runtime/imports/resolveSSRValue.js +0 -8
- package/dist/runtime/render/renderRscThenableToHtmlStream.js +0 -54
- package/dist/runtime/render/transformRscToHtmlStream.d.ts +0 -8
- package/dist/runtime/render/transformRscToHtmlStream.js +0 -19
- /package/dist/lib/{normalizeModulePath.test.d.ts → e2e/types.mjs} +0 -0
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");
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Browser } from "puppeteer-core";
|
|
2
|
+
import { SmokeTestOptions } from "./types.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Launch a browser instance
|
|
5
|
+
*/
|
|
6
|
+
export declare function launchBrowser(browserPath?: string, headless?: boolean): Promise<Browser>;
|
|
7
|
+
/**
|
|
8
|
+
* Get the browser executable path
|
|
9
|
+
*/
|
|
10
|
+
export declare function getBrowserPath(testOptions?: SmokeTestOptions): Promise<string>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { computeExecutablePath, detectBrowserPlatform, install, Browser as PuppeteerBrowser, resolveBuildId, } from "@puppeteer/browsers";
|
|
2
|
+
import debug from "debug";
|
|
3
|
+
import { mkdirp, pathExists } from "fs-extra";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import puppeteer from "puppeteer-core";
|
|
7
|
+
const log = debug("rwsdk:e2e:browser");
|
|
8
|
+
/**
|
|
9
|
+
* Launch a browser instance
|
|
10
|
+
*/
|
|
11
|
+
export async function launchBrowser(browserPath, headless = true) {
|
|
12
|
+
// Get browser path if not provided
|
|
13
|
+
if (!browserPath) {
|
|
14
|
+
log("Getting browser executable path");
|
|
15
|
+
browserPath = await getBrowserPath({ headless });
|
|
16
|
+
}
|
|
17
|
+
console.log(`🚀 Launching browser from ${browserPath} (headless: ${headless})`);
|
|
18
|
+
log("Starting browser with puppeteer");
|
|
19
|
+
return await puppeteer.launch({
|
|
20
|
+
executablePath: browserPath,
|
|
21
|
+
headless,
|
|
22
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get the browser executable path
|
|
27
|
+
*/
|
|
28
|
+
export async function getBrowserPath(testOptions) {
|
|
29
|
+
console.log("Finding Chrome executable...");
|
|
30
|
+
// First try using environment variable if set
|
|
31
|
+
if (process.env.CHROME_PATH) {
|
|
32
|
+
console.log(`Using Chrome from environment variable: ${process.env.CHROME_PATH}`);
|
|
33
|
+
return process.env.CHROME_PATH;
|
|
34
|
+
}
|
|
35
|
+
// Detect platform
|
|
36
|
+
log("Detecting platform");
|
|
37
|
+
const platform = detectBrowserPlatform();
|
|
38
|
+
if (!platform) {
|
|
39
|
+
log("ERROR: Failed to detect browser platform");
|
|
40
|
+
throw new Error("Failed to detect browser platform");
|
|
41
|
+
}
|
|
42
|
+
log("Detected platform: %s", platform);
|
|
43
|
+
// Define a consistent cache directory path in system temp folder
|
|
44
|
+
const rwCacheDir = join(os.tmpdir(), "redwoodjs-smoke-test-cache");
|
|
45
|
+
await mkdirp(rwCacheDir);
|
|
46
|
+
log("Using cache directory: %s", rwCacheDir);
|
|
47
|
+
// Determine browser type based on headless option
|
|
48
|
+
const browser = testOptions?.headless === false
|
|
49
|
+
? PuppeteerBrowser.CHROME
|
|
50
|
+
: PuppeteerBrowser.CHROMEHEADLESSSHELL;
|
|
51
|
+
log(`Using browser type: ${browser}`);
|
|
52
|
+
// Resolve the buildId for the stable Chrome version - do this once
|
|
53
|
+
log("Resolving Chrome buildId for stable channel");
|
|
54
|
+
const buildId = await resolveBuildId(browser, platform, "stable");
|
|
55
|
+
log("Resolved buildId: %s", buildId);
|
|
56
|
+
// Create installation options - use them consistently
|
|
57
|
+
const installOptions = {
|
|
58
|
+
browser,
|
|
59
|
+
platform,
|
|
60
|
+
cacheDir: rwCacheDir,
|
|
61
|
+
buildId,
|
|
62
|
+
unpack: true,
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
// Try to compute the path first (this will check if it's installed)
|
|
66
|
+
log("Attempting to find existing Chrome installation");
|
|
67
|
+
const path = computeExecutablePath(installOptions);
|
|
68
|
+
if (await pathExists(path)) {
|
|
69
|
+
console.log(`Found existing Chrome at: ${path}`);
|
|
70
|
+
return path;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
throw new Error("Chrome not found at: " + path);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// If path computation fails, install Chrome
|
|
78
|
+
console.log("No Chrome installation found. Installing Chrome...");
|
|
79
|
+
// Add better error handling for the install step
|
|
80
|
+
try {
|
|
81
|
+
console.log("Downloading Chrome (this may take a few minutes)...");
|
|
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
|
+
}
|
|
100
|
+
console.log("✅ Chrome installation completed successfully");
|
|
101
|
+
// Now compute the path for the installed browser
|
|
102
|
+
const path = computeExecutablePath(installOptions);
|
|
103
|
+
console.log(`Installed and using Chrome at: ${path}`);
|
|
104
|
+
return path;
|
|
105
|
+
}
|
|
106
|
+
catch (installError) {
|
|
107
|
+
// Provide more detailed error about the browser download failure
|
|
108
|
+
log("ERROR: Failed to download/install Chrome: %O", installError);
|
|
109
|
+
console.error(`❌ Failed to download/install Chrome browser.`);
|
|
110
|
+
console.error(`This is likely a network issue or the browser download URL is unavailable.`);
|
|
111
|
+
console.error(`Error details: ${installError instanceof Error ? installError.message : String(installError)}`);
|
|
112
|
+
// For debug builds, show the full error stack if available
|
|
113
|
+
if (installError instanceof Error && installError.stack) {
|
|
114
|
+
log("Error stack: %s", installError.stack);
|
|
115
|
+
}
|
|
116
|
+
console.log("\nPossible solutions:");
|
|
117
|
+
console.log("1. Check your internet connection");
|
|
118
|
+
console.log("2. Set CHROME_PATH environment variable to an existing Chrome installation");
|
|
119
|
+
console.log("3. Install Chrome manually and run the tests again");
|
|
120
|
+
throw new Error(`Failed to install Chrome browser: ${installError instanceof Error ? installError.message : String(installError)}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import debug from "debug";
|
|
2
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
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;
|
|
8
|
+
const log = debug("rwsdk:e2e:dev");
|
|
9
|
+
/**
|
|
10
|
+
* Run the local development server and return the URL
|
|
11
|
+
*/
|
|
12
|
+
export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
13
|
+
console.log("🚀 Starting development server...");
|
|
14
|
+
// Function to stop the dev server - defined early so we can use it in error handling
|
|
15
|
+
let devProcess = null;
|
|
16
|
+
let isErrorExpected = false;
|
|
17
|
+
const stopDev = async () => {
|
|
18
|
+
isErrorExpected = true;
|
|
19
|
+
if (!devProcess || !devProcess.pid) {
|
|
20
|
+
log("No dev process to stop or PID is missing");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
console.log("Stopping development server...");
|
|
24
|
+
try {
|
|
25
|
+
// Send a regular termination signal to the entire process group first
|
|
26
|
+
process.kill(-devProcess.pid, "SIGTERM");
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
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
|
+
}
|
|
60
|
+
}
|
|
61
|
+
console.log("Development server stopped");
|
|
62
|
+
};
|
|
63
|
+
try {
|
|
64
|
+
// Check if we're in CI mode
|
|
65
|
+
const inCIMode = process.env.CI === "true" || process.env.CI === "1";
|
|
66
|
+
// Start dev server with stdout pipe to capture URL
|
|
67
|
+
// Create environment variables object
|
|
68
|
+
const env = {
|
|
69
|
+
...process.env,
|
|
70
|
+
NODE_ENV: "development",
|
|
71
|
+
};
|
|
72
|
+
// Disable colors when running in CI mode to make URL parsing more reliable
|
|
73
|
+
if (inCIMode) {
|
|
74
|
+
log("Running in CI mode, disabling colors for dev server output");
|
|
75
|
+
env.NO_COLOR = "1";
|
|
76
|
+
env.FORCE_COLOR = "0";
|
|
77
|
+
}
|
|
78
|
+
// Map package manager names to actual commands
|
|
79
|
+
const getPackageManagerCommand = (pm) => {
|
|
80
|
+
switch (pm) {
|
|
81
|
+
case "yarn-classic":
|
|
82
|
+
return "yarn";
|
|
83
|
+
default:
|
|
84
|
+
return pm;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const pm = getPackageManagerCommand(packageManager);
|
|
88
|
+
// Use the provided cwd if available
|
|
89
|
+
devProcess = $({
|
|
90
|
+
all: true,
|
|
91
|
+
detached: true, // Run in a new process group so we can kill the entire group
|
|
92
|
+
cleanup: false, // Don't auto-kill on exit
|
|
93
|
+
cwd: cwd || process.cwd(), // Use provided directory or current directory
|
|
94
|
+
env, // Pass the updated environment variables
|
|
95
|
+
stdio: "pipe", // Ensure streams are piped
|
|
96
|
+
}) `${pm} run dev`;
|
|
97
|
+
devProcess.catch((error) => {
|
|
98
|
+
if (!isErrorExpected) {
|
|
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);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
log("Development server process spawned in directory: %s", cwd || process.cwd());
|
|
106
|
+
// Store chunks to parse the URL
|
|
107
|
+
let url = "";
|
|
108
|
+
let allOutput = "";
|
|
109
|
+
// Listen for all output to get the URL
|
|
110
|
+
const handleOutput = (data, source) => {
|
|
111
|
+
const output = data.toString();
|
|
112
|
+
console.log(output);
|
|
113
|
+
allOutput += output; // Accumulate all output
|
|
114
|
+
log("Received output from %s: %s", source, output.replace(/\n/g, "\\n"));
|
|
115
|
+
if (!url) {
|
|
116
|
+
// Multiple patterns to catch different package manager outputs
|
|
117
|
+
const patterns = [
|
|
118
|
+
// Standard Vite output: "Local: http://localhost:5173/"
|
|
119
|
+
/Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
120
|
+
// Alternative Vite output: "➜ Local: http://localhost:5173/"
|
|
121
|
+
/[➜→]\s*Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
122
|
+
// Unicode-safe arrow pattern
|
|
123
|
+
/[\u27A1\u2192\u279C]\s*Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
124
|
+
// Direct URL pattern: "http://localhost:5173"
|
|
125
|
+
/(https?:\/\/localhost:\d+)/i,
|
|
126
|
+
// Port-only pattern: "localhost:5173"
|
|
127
|
+
/localhost:(\d+)/i,
|
|
128
|
+
// Server ready messages
|
|
129
|
+
/server.*ready.*localhost:(\d+)/i,
|
|
130
|
+
/dev server.*localhost:(\d+)/i,
|
|
131
|
+
];
|
|
132
|
+
for (const pattern of patterns) {
|
|
133
|
+
const match = output.match(pattern);
|
|
134
|
+
log("Testing pattern %s against output: %s", pattern.source, output.replace(/\n/g, "\\n"));
|
|
135
|
+
if (match) {
|
|
136
|
+
log("Pattern matched: %s, groups: %o", pattern.source, match);
|
|
137
|
+
if (match[1] && match[1].startsWith("http")) {
|
|
138
|
+
url = match[1];
|
|
139
|
+
log("Found development server URL with pattern %s: %s", pattern.source, url);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
else if (match[1] && /^\d+$/.test(match[1])) {
|
|
143
|
+
url = `http://localhost:${match[1]}`;
|
|
144
|
+
log("Found development server URL with port pattern %s: %s", pattern.source, url);
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Log potential matches for debugging
|
|
150
|
+
if (!url &&
|
|
151
|
+
(output.includes("localhost") ||
|
|
152
|
+
output.includes("Local") ||
|
|
153
|
+
output.includes("server"))) {
|
|
154
|
+
log("Potential URL pattern found but not matched: %s", output.trim());
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
// Listen to all possible output streams
|
|
159
|
+
log("Setting up stream listeners. Available streams: all=%s, stdout=%s, stderr=%s", !!devProcess.all, !!devProcess.stdout, !!devProcess.stderr);
|
|
160
|
+
devProcess.all?.on("data", (data) => handleOutput(data, "all"));
|
|
161
|
+
devProcess.stdout?.on("data", (data) => handleOutput(data, "stdout"));
|
|
162
|
+
devProcess.stderr?.on("data", (data) => handleOutput(data, "stderr"));
|
|
163
|
+
// Also try listening to the raw process output
|
|
164
|
+
if (devProcess.child) {
|
|
165
|
+
log("Setting up child process stream listeners");
|
|
166
|
+
devProcess.child.stdout?.on("data", (data) => handleOutput(data, "child.stdout"));
|
|
167
|
+
devProcess.child.stderr?.on("data", (data) => handleOutput(data, "child.stderr"));
|
|
168
|
+
}
|
|
169
|
+
// Wait for URL with timeout
|
|
170
|
+
const waitForUrl = async () => {
|
|
171
|
+
const start = Date.now();
|
|
172
|
+
const timeout = 60000; // 60 seconds
|
|
173
|
+
while (Date.now() - start < timeout) {
|
|
174
|
+
if (url) {
|
|
175
|
+
return url;
|
|
176
|
+
}
|
|
177
|
+
// Fallback: check accumulated output if stream listeners aren't working
|
|
178
|
+
if (!url && allOutput) {
|
|
179
|
+
log("Checking accumulated output for URL patterns: %s", allOutput.replace(/\n/g, "\\n"));
|
|
180
|
+
const patterns = [
|
|
181
|
+
/Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
182
|
+
/[➜→]\s*Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
183
|
+
/[\u27A1\u2192\u279C]\s*Local:\s*(?:\u001b\[\d+m)?(https?:\/\/localhost:\d+)/i,
|
|
184
|
+
/(https?:\/\/localhost:\d+)/i,
|
|
185
|
+
/localhost:(\d+)/i,
|
|
186
|
+
];
|
|
187
|
+
for (const pattern of patterns) {
|
|
188
|
+
const match = allOutput.match(pattern);
|
|
189
|
+
if (match) {
|
|
190
|
+
if (match[1] && match[1].startsWith("http")) {
|
|
191
|
+
url = match[1];
|
|
192
|
+
log("Found URL in accumulated output with pattern %s: %s", pattern.source, url);
|
|
193
|
+
return url;
|
|
194
|
+
}
|
|
195
|
+
else if (match[1] && /^\d+$/.test(match[1])) {
|
|
196
|
+
url = `http://localhost:${match[1]}`;
|
|
197
|
+
log("Found URL in accumulated output with port pattern %s: %s", pattern.source, url);
|
|
198
|
+
return url;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Check if the process is still running
|
|
204
|
+
if (devProcess.exitCode !== null) {
|
|
205
|
+
log("ERROR: Development server process exited with code %d. Final output: %s", devProcess.exitCode, allOutput);
|
|
206
|
+
throw new Error(`Development server process exited with code ${devProcess.exitCode}`);
|
|
207
|
+
}
|
|
208
|
+
await sleep(500); // Check every 500ms
|
|
209
|
+
}
|
|
210
|
+
log("ERROR: Timed out waiting for dev server URL. Final accumulated output: %s", allOutput);
|
|
211
|
+
throw new Error("Timed out waiting for dev server URL");
|
|
212
|
+
};
|
|
213
|
+
// Wait for the URL
|
|
214
|
+
const serverUrl = await waitForUrl();
|
|
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
|
+
});
|
|
232
|
+
return { url: serverUrl, stopDev };
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
// Make sure to try to stop the server on error
|
|
236
|
+
log("Error during dev server startup: %O", error);
|
|
237
|
+
await stopDev().catch((e) => {
|
|
238
|
+
log("Failed to stop dev server during error handling: %O", e);
|
|
239
|
+
});
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import tmp from "tmp-promise";
|
|
2
|
+
import { PackageManager } from "./types.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Copy project to a temporary directory with a unique name
|
|
5
|
+
*/
|
|
6
|
+
export declare function copyProjectToTempDir(projectDir: string, resourceUniqueKey: string, packageManager?: PackageManager, monorepoRoot?: string): Promise<{
|
|
7
|
+
tempDir: tmp.DirectoryResult;
|
|
8
|
+
targetDir: string;
|
|
9
|
+
workerName: string;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import debug from "debug";
|
|
2
|
+
import { copy, pathExists } from "fs-extra";
|
|
3
|
+
import ignore from "ignore";
|
|
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
|
+
import { $ } from "../../lib/$.mjs";
|
|
10
|
+
import { ROOT_DIR } from "../constants.mjs";
|
|
11
|
+
import { retry } from "./retry.mjs";
|
|
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
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Copy project to a temporary directory with a unique name
|
|
35
|
+
*/
|
|
36
|
+
export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager, monorepoRoot) {
|
|
37
|
+
const { tarballPath, cleanupTarball } = await createSdkTarball();
|
|
38
|
+
try {
|
|
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);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
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"));
|
|
75
|
+
}
|
|
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");
|
|
105
|
+
}
|
|
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");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
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");
|
|
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 };
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
await cleanupTarball();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function installDependencies(targetDir, packageManager = "pnpm") {
|
|
149
|
+
console.log(`📦 Installing project dependencies in ${targetDir} using ${packageManager}...`);
|
|
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 });
|
|
183
|
+
const installCommand = {
|
|
184
|
+
pnpm: ["pnpm", "install"],
|
|
185
|
+
npm: ["npm", "install", "--cache", npmCacheDir],
|
|
186
|
+
yarn: ["yarn", "install"],
|
|
187
|
+
"yarn-classic": ["yarn"],
|
|
188
|
+
}[packageManager];
|
|
189
|
+
// Run install command in the target directory
|
|
190
|
+
log(`Running ${installCommand.join(" ")}`);
|
|
191
|
+
const [command, ...args] = installCommand;
|
|
192
|
+
const result = await $(command, args, {
|
|
193
|
+
cwd: targetDir,
|
|
194
|
+
stdio: "pipe", // Capture output
|
|
195
|
+
env: {
|
|
196
|
+
YARN_ENABLE_HARDENED_MODE: "0",
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
console.log("✅ Dependencies installed successfully");
|
|
200
|
+
// Log installation details at debug level
|
|
201
|
+
if (result.stdout) {
|
|
202
|
+
log(`${packageManager} install output: %s`, result.stdout);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
log("ERROR: Failed to install dependencies: %O", error);
|
|
207
|
+
console.error(`❌ Failed to install dependencies: ${error instanceof Error ? error.message : String(error)}`);
|
|
208
|
+
throw new Error(`Failed to install project dependencies. Please ensure the project can be installed with ${packageManager}.`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -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>;
|