@farm.js/cli 0.1.0-beta.16 → 0.1.0-beta.17

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/index.mjs CHANGED
@@ -5,10 +5,10 @@ import { createServer, startDevServer } from "@farm.js/core/server";
5
5
  import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "node:fs";
6
6
  import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
7
7
  import path from "node:path";
8
- import { execFileSync, execSync } from "child_process";
8
+ import { execFileSync } from "child_process";
9
9
  import { existsSync as existsSync$1, readFileSync as readFileSync$1 } from "fs";
10
10
  import path$1 from "path";
11
- import { farmRouteRuleMatches, generateFarmTypeArtifacts, getFarmDocsRouteTypeEntries, getFarmPresetRuntime, getFarmSourceRoots, getIntegrationSchemas, getPresetForDeployTarget, isProgrammaticRoutesFileName, loadConfig, logger, mergeFarmRouteRuntimeConfigs, normalizeDeployTarget, resolveConfig, resolveDeployConfig, resolveDeployOutputPath, resolveFarmRouteRuleRuntimeConfig, resolveFarmRouteRuntimeConfig, resolveRouteRenderingConfig, scanProgrammaticPagePaths } from "@farm.js/core";
11
+ import { farmRouteRuleMatches, generateFarmTypeArtifacts, getFarmDocsRouteTypeEntries, getFarmPresetRuntime, getFarmSourceRoots, getIntegrationSchemas, getPresetForDeployTarget, loadConfig, logger, mergeFarmRouteRuntimeConfigs, normalizeDeployTarget, resolveConfig, resolveDeployConfig, resolveDeployOutputPath, resolveFarmRouteRuleRuntimeConfig, resolveFarmRouteRuntimeConfig, resolveRouteRenderingConfig, scanProgrammaticPagePaths } from "@farm.js/core";
12
12
  import { spawn, spawnSync } from "node:child_process";
13
13
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
14
14
  import pc from "picocolors";
@@ -18,6 +18,15 @@ import { pathToFileURL } from "node:url";
18
18
  var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
19
19
  //#endregion
20
20
  //#region src/deploy.ts
21
+ var FarmDeployError = class extends Error {
22
+ constructor(code, platform, message, options) {
23
+ super(message);
24
+ this.name = "FarmDeployError";
25
+ this.code = code;
26
+ this.platform = platform;
27
+ this.cause = options?.cause;
28
+ }
29
+ };
21
30
  /**
22
31
  * Deploy Farm.js application
23
32
  */
@@ -32,7 +41,7 @@ async function deployFarm(options = {}) {
32
41
  root: plan.root,
33
42
  preset: plan.preset
34
43
  });
35
- if (!existsSync$1(plan.outputDir)) throw new Error(`Build output not found at ${plan.outputDir}. Please run 'farm build' first.`);
44
+ if (!existsSync$1(plan.outputDir)) throw new FarmDeployError("INVALID_BUILD_OUTPUT", plan.target, `Build output not found at ${plan.outputDir}. Please run 'farm build' first.`);
36
45
  await deployPlatform(plan.target, plan.root, plan.outputDir, deployConfig, options.prod);
37
46
  return plan;
38
47
  }
@@ -94,26 +103,64 @@ async function resolveFarmDeployContext(options) {
94
103
  };
95
104
  }
96
105
  function createDeployCommand(platform, root, outputDir, deployConfig, prod, cloudflareAgent) {
97
- if (platform === "vercel") return {
98
- command: `vercel deploy --prebuilt --yes${prod ? " --prod" : ""}`,
99
- cwd: path$1.resolve(root)
100
- };
106
+ if (platform === "vercel") {
107
+ const args = [
108
+ "deploy",
109
+ "--prebuilt",
110
+ "--yes",
111
+ ...prod ? ["--prod"] : []
112
+ ];
113
+ return {
114
+ command: formatCommand$1("vercel", args),
115
+ cwd: path$1.resolve(root),
116
+ executable: "vercel",
117
+ args
118
+ };
119
+ }
101
120
  if (platform === "netlify") {
102
- const site = deployConfig.netlify?.site;
121
+ const args = createNetlifyDeployArgs(deployConfig.netlify?.site);
103
122
  return {
104
- command: `netlify deploy --prod --dir=.${site ? ` --site=${site}` : ""}`,
105
- cwd: outputDir
123
+ command: formatCommand$1("netlify", args),
124
+ cwd: outputDir,
125
+ executable: "netlify",
126
+ args
106
127
  };
107
128
  }
108
- if (cloudflareAgent) return {
109
- command: `wrangler deploy --config ${cloudflareAgent.configPath}${cloudflareAgent.environment ? ` --env ${cloudflareAgent.environment}` : ""}`,
110
- cwd: path$1.resolve(root)
111
- };
129
+ if (cloudflareAgent) {
130
+ const args = [
131
+ "deploy",
132
+ "--config",
133
+ cloudflareAgent.configPath,
134
+ ...cloudflareAgent.environment ? ["--env", cloudflareAgent.environment] : []
135
+ ];
136
+ return {
137
+ command: formatCommand$1("wrangler", args),
138
+ cwd: path$1.resolve(root),
139
+ executable: "wrangler",
140
+ args
141
+ };
142
+ }
143
+ const args = [
144
+ "pages",
145
+ "deploy",
146
+ ".",
147
+ `--project-name=${deployConfig.cloudflare?.projectName || deployConfig.projectName || "farm-app"}`
148
+ ];
112
149
  return {
113
- command: `wrangler pages deploy . --project-name=${deployConfig.cloudflare?.projectName || deployConfig.projectName || "farm-app"}`,
114
- cwd: outputDir
150
+ command: formatCommand$1("wrangler", args),
151
+ cwd: outputDir,
152
+ executable: "wrangler",
153
+ args
115
154
  };
116
155
  }
156
+ function createNetlifyDeployArgs(site) {
157
+ return [
158
+ "deploy",
159
+ "--prod",
160
+ "--dir=.",
161
+ ...site ? [`--site=${site}`] : []
162
+ ];
163
+ }
117
164
  /**
118
165
  * Deploy using platform's native CLI (user credentials)
119
166
  */
@@ -134,19 +181,20 @@ async function deployPlatform(platform, root, outputDir, deployConfig, prod) {
134
181
  async function deployVercel(root, outputDir, prod) {
135
182
  logger.info("🚀 Deploying to Vercel...");
136
183
  try {
137
- execSync("vercel --version", { stdio: "ignore" });
138
- } catch {
139
- logger.error("❌ Vercel CLI is not installed.");
140
- logger.info("💡 Install it with: npm i -g vercel");
141
- process.exit(1);
184
+ execFileSync("vercel", ["--version"], {
185
+ stdio: "ignore",
186
+ cwd: root
187
+ });
188
+ } catch (error) {
189
+ throw new FarmDeployError("CLI_NOT_INSTALLED", "vercel", "Vercel CLI is not installed. Install it with: npm i -g vercel", { cause: error });
142
190
  }
143
191
  try {
144
- execSync("vercel whoami", { stdio: "ignore" });
145
- } catch {
146
- logger.warn("⚠️ Not logged in to Vercel.");
147
- logger.info("💡 Please run: vercel login");
148
- logger.info(" Then run: farm deploy --vercel");
149
- process.exit(1);
192
+ execFileSync("vercel", ["whoami"], {
193
+ stdio: "ignore",
194
+ cwd: root
195
+ });
196
+ } catch (error) {
197
+ throw new FarmDeployError("CLI_NOT_AUTHENTICATED", "vercel", "Vercel CLI is not authenticated. Run 'vercel login', then retry the deployment.", { cause: error });
150
198
  }
151
199
  try {
152
200
  const { existsSync, statSync, readdirSync } = await import("fs");
@@ -155,14 +203,8 @@ async function deployVercel(root, outputDir, prod) {
155
203
  const configFile = path$1.join(outputDir, "config.json");
156
204
  const serverIndex = path$1.join(functionsDir, "index.mjs");
157
205
  logger.info("🔍 Verifying deployment structure...");
158
- if (!existsSync(functionsDir)) {
159
- logger.error(`❌ Functions directory not found at ${functionsDir}`);
160
- process.exit(1);
161
- }
162
- if (!existsSync(serverIndex)) {
163
- logger.error(`❌ Server entry point not found at ${serverIndex}`);
164
- process.exit(1);
165
- }
206
+ if (!existsSync(functionsDir)) throw new FarmDeployError("INVALID_BUILD_OUTPUT", "vercel", `Functions directory not found at ${functionsDir}.`);
207
+ if (!existsSync(serverIndex)) throw new FarmDeployError("INVALID_BUILD_OUTPUT", "vercel", `Server entry point not found at ${serverIndex}.`);
166
208
  logger.info(`✅ Functions directory: ${functionsDir}`);
167
209
  if (!existsSync(staticDir)) logger.warn(`⚠️ Static directory not found at ${staticDir}`);
168
210
  else {
@@ -243,14 +285,19 @@ async function deployVercel(root, outputDir, prod) {
243
285
  logger.info(` Static: ${staticDir}`);
244
286
  logger.info(` Config: ${configFile}`);
245
287
  logger.info("📤 Uploading to Vercel...");
246
- execSync(`vercel deploy --prebuilt --yes${prod ? " --prod" : ""}`, {
288
+ execFileSync("vercel", [
289
+ "deploy",
290
+ "--prebuilt",
291
+ "--yes",
292
+ ...prod ? ["--prod"] : []
293
+ ], {
247
294
  stdio: "inherit",
248
295
  cwd: root
249
296
  });
250
297
  logger.success("✅ Deployed to Vercel successfully!");
251
298
  } catch (error) {
252
- logger.error(`❌ Failed to deploy to Vercel: ${error.message}`);
253
- process.exit(1);
299
+ if (error instanceof FarmDeployError) throw error;
300
+ throw new FarmDeployError("DEPLOY_FAILED", "vercel", `Failed to deploy to Vercel: ${getErrorMessage(error)}`, { cause: error });
254
301
  }
255
302
  }
256
303
  /** Deploy to a composed Worker or fall back to Cloudflare Pages. */
@@ -271,8 +318,7 @@ async function deployCloudflare(root, outputDir, projectName) {
271
318
  });
272
319
  logger.success("✅ Deployed Farm and Cloudflare Agents successfully!");
273
320
  } catch (error) {
274
- logger.error(`❌ Failed to deploy to Cloudflare: ${error.message}`);
275
- process.exit(1);
321
+ throw new FarmDeployError("DEPLOY_FAILED", "cloudflare", `Failed to deploy to Cloudflare: ${getErrorMessage(error)}`, { cause: error });
276
322
  }
277
323
  return;
278
324
  }
@@ -290,8 +336,7 @@ async function deployCloudflare(root, outputDir, projectName) {
290
336
  });
291
337
  logger.success("✅ Deployed to Cloudflare Pages successfully!");
292
338
  } catch (error) {
293
- logger.error(`❌ Failed to deploy to Cloudflare: ${error.message}`);
294
- process.exit(1);
339
+ throw new FarmDeployError("DEPLOY_FAILED", "cloudflare", `Failed to deploy to Cloudflare: ${getErrorMessage(error)}`, { cause: error });
295
340
  }
296
341
  }
297
342
  /** Read the trusted Workers deployment handoff emitted by @farm.js/cf-agent. */
@@ -343,10 +388,8 @@ function assertWranglerInstalled(root) {
343
388
  stdio: "ignore",
344
389
  cwd: root
345
390
  });
346
- } catch {
347
- logger.error(" Wrangler CLI is not installed.");
348
- logger.info("💡 Install it in this project with: npm i -D wrangler");
349
- process.exit(1);
391
+ } catch (error) {
392
+ throw new FarmDeployError("CLI_NOT_INSTALLED", "cloudflare", "Wrangler CLI is not installed. Install it in this project with: npm i -D wrangler", { cause: error });
350
393
  }
351
394
  }
352
395
  function isRecord(value) {
@@ -358,22 +401,33 @@ function isRecord(value) {
358
401
  async function deployNetlify(root, outputDir, site) {
359
402
  logger.info("🚀 Deploying to Netlify...");
360
403
  try {
361
- execSync("netlify --version", { stdio: "ignore" });
362
- } catch {
363
- logger.error("❌ Netlify CLI is not installed.");
364
- logger.info("💡 Install it with: npm i -g netlify-cli");
365
- process.exit(1);
404
+ execFileSync("netlify", ["--version"], {
405
+ stdio: "ignore",
406
+ cwd: root
407
+ });
408
+ } catch (error) {
409
+ throw new FarmDeployError("CLI_NOT_INSTALLED", "netlify", "Netlify CLI is not installed. Install it with: npm i -g netlify-cli", { cause: error });
366
410
  }
367
411
  try {
368
- process.chdir(outputDir);
369
- const siteFlag = site ? ` --site=${site}` : "";
370
- execSync(`netlify deploy --prod --dir=.${siteFlag}`, { stdio: "inherit" });
412
+ execFileSync("netlify", createNetlifyDeployArgs(site), {
413
+ stdio: "inherit",
414
+ cwd: outputDir
415
+ });
371
416
  logger.success("✅ Deployed to Netlify successfully!");
372
417
  } catch (error) {
373
- logger.error(`❌ Failed to deploy to Netlify: ${error.message}`);
374
- process.exit(1);
418
+ throw new FarmDeployError("DEPLOY_FAILED", "netlify", `Failed to deploy to Netlify: ${getErrorMessage(error)}`, { cause: error });
375
419
  }
376
420
  }
421
+ function formatCommand$1(executable, args) {
422
+ return [executable, ...args].map(formatCommandArgument).join(" ");
423
+ }
424
+ function formatCommandArgument(argument) {
425
+ if (/^[A-Za-z0-9_./:=@+-]+$/.test(argument)) return argument;
426
+ return `'${argument.replace(/'/g, `'"'"'`)}'`;
427
+ }
428
+ function getErrorMessage(error) {
429
+ return error instanceof Error ? error.message : String(error);
430
+ }
377
431
  //#endregion
378
432
  //#region src/preview-gateway.ts
379
433
  const DEFAULT_GATEWAY_URL = "https://preview.farming-labs.dev";
@@ -2071,9 +2125,8 @@ function discoverMatchingPages(config, pathname) {
2071
2125
  }
2072
2126
  const sourceDirectory = path.join(source.root, source.srcDir);
2073
2127
  if (!existsSync(sourceDirectory)) continue;
2074
- for (const entry of readdirSync(sourceDirectory, { withFileTypes: true })) {
2075
- if (!entry.isFile() || !isProgrammaticRoutesFileName(entry.name)) continue;
2076
- const filePath = path.join(sourceDirectory, entry.name);
2128
+ for (const filePath of walkFiles(sourceDirectory)) {
2129
+ if (!/\.(?:tsx?|jsx?)$/.test(filePath) || filePath.endsWith(".d.ts")) continue;
2077
2130
  const moduleSource = readFileSync(filePath, "utf8");
2078
2131
  for (const pattern of scanProgrammaticPagePaths(moduleSource)) {
2079
2132
  const match = matchRoutePattern(pattern, pathname);
@@ -3152,6 +3205,6 @@ function formatError(error) {
3152
3205
  return error instanceof Error ? error.message : String(error);
3153
3206
  }
3154
3207
  //#endregion
3155
- export { FarmGeneratedArtifactsStaleError, addFarmIntegration, buildFarm, createFarmDeployPlan, createFarmUpgradePlan, createFrameworkMigrationPlan, createGatewaySession, createPreviewGatewayPlan, createPreviewTunnelPlan, createServer, deployFarm, detectFarmPackageManager, explainFarmRoute, formatFarmCronJobs, formatFarmDeployPlan, formatFarmDoctorReport, formatFarmRouteExplanation, formatFarmUpgradePlan, forwardGatewayRequest, generateFarmArtifacts, inspectFrameworkMigrations, listFarmCronJobs, listFarmIntegrationProviders, loadFarmCronConfig, migrateFarm, migrateFarmAuth, parsePreviewPublicUrl, previewFarm, resolveCloudflareAgentDeployPlan, resolvePreviewTarget, runFarmCronJob, runFarmDoctor, runPreviewGateway, startDevServer, startFarmCronScheduler, upgradeFarm };
3208
+ export { FarmDeployError, FarmGeneratedArtifactsStaleError, addFarmIntegration, buildFarm, createFarmDeployPlan, createFarmUpgradePlan, createFrameworkMigrationPlan, createGatewaySession, createPreviewGatewayPlan, createPreviewTunnelPlan, createServer, deployFarm, detectFarmPackageManager, explainFarmRoute, formatFarmCronJobs, formatFarmDeployPlan, formatFarmDoctorReport, formatFarmRouteExplanation, formatFarmUpgradePlan, forwardGatewayRequest, generateFarmArtifacts, inspectFrameworkMigrations, listFarmCronJobs, listFarmIntegrationProviders, loadFarmCronConfig, migrateFarm, migrateFarmAuth, parsePreviewPublicUrl, previewFarm, resolveCloudflareAgentDeployPlan, resolvePreviewTarget, runFarmCronJob, runFarmDoctor, runPreviewGateway, startDevServer, startFarmCronScheduler, upgradeFarm };
3156
3209
 
3157
3210
  //# sourceMappingURL=index.mjs.map