@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/bin/farm.js CHANGED
@@ -468,8 +468,10 @@ program
468
468
  const { deployFarm } = require("../dist/index.js");
469
469
  await deployFarm(options);
470
470
  } catch (error) {
471
- console.error("Failed to deploy:", error);
472
- process.exit(1);
471
+ const code = error?.name === "FarmDeployError" ? ` [${error.code}]` : "";
472
+ const message = error instanceof Error ? error.message : String(error);
473
+ console.error(`Failed to deploy${code}: ${message}`);
474
+ process.exitCode = 1;
473
475
  }
474
476
  });
475
477
 
package/dist/index.js CHANGED
@@ -19,6 +19,15 @@ let croner = require("croner");
19
19
  let node_module = require("node:module");
20
20
  let node_url = require("node:url");
21
21
  //#region src/deploy.ts
22
+ var FarmDeployError = class extends Error {
23
+ constructor(code, platform, message, options) {
24
+ super(message);
25
+ this.name = "FarmDeployError";
26
+ this.code = code;
27
+ this.platform = platform;
28
+ this.cause = options?.cause;
29
+ }
30
+ };
22
31
  /**
23
32
  * Deploy Farm.js application
24
33
  */
@@ -33,7 +42,7 @@ async function deployFarm(options = {}) {
33
42
  root: plan.root,
34
43
  preset: plan.preset
35
44
  });
36
- if (!(0, fs.existsSync)(plan.outputDir)) throw new Error(`Build output not found at ${plan.outputDir}. Please run 'farm build' first.`);
45
+ if (!(0, fs.existsSync)(plan.outputDir)) throw new FarmDeployError("INVALID_BUILD_OUTPUT", plan.target, `Build output not found at ${plan.outputDir}. Please run 'farm build' first.`);
37
46
  await deployPlatform(plan.target, plan.root, plan.outputDir, deployConfig, options.prod);
38
47
  return plan;
39
48
  }
@@ -95,26 +104,64 @@ async function resolveFarmDeployContext(options) {
95
104
  };
96
105
  }
97
106
  function createDeployCommand(platform, root, outputDir, deployConfig, prod, cloudflareAgent) {
98
- if (platform === "vercel") return {
99
- command: `vercel deploy --prebuilt --yes${prod ? " --prod" : ""}`,
100
- cwd: path.default.resolve(root)
101
- };
107
+ if (platform === "vercel") {
108
+ const args = [
109
+ "deploy",
110
+ "--prebuilt",
111
+ "--yes",
112
+ ...prod ? ["--prod"] : []
113
+ ];
114
+ return {
115
+ command: formatCommand$1("vercel", args),
116
+ cwd: path.default.resolve(root),
117
+ executable: "vercel",
118
+ args
119
+ };
120
+ }
102
121
  if (platform === "netlify") {
103
- const site = deployConfig.netlify?.site;
122
+ const args = createNetlifyDeployArgs(deployConfig.netlify?.site);
104
123
  return {
105
- command: `netlify deploy --prod --dir=.${site ? ` --site=${site}` : ""}`,
106
- cwd: outputDir
124
+ command: formatCommand$1("netlify", args),
125
+ cwd: outputDir,
126
+ executable: "netlify",
127
+ args
107
128
  };
108
129
  }
109
- if (cloudflareAgent) return {
110
- command: `wrangler deploy --config ${cloudflareAgent.configPath}${cloudflareAgent.environment ? ` --env ${cloudflareAgent.environment}` : ""}`,
111
- cwd: path.default.resolve(root)
112
- };
130
+ if (cloudflareAgent) {
131
+ const args = [
132
+ "deploy",
133
+ "--config",
134
+ cloudflareAgent.configPath,
135
+ ...cloudflareAgent.environment ? ["--env", cloudflareAgent.environment] : []
136
+ ];
137
+ return {
138
+ command: formatCommand$1("wrangler", args),
139
+ cwd: path.default.resolve(root),
140
+ executable: "wrangler",
141
+ args
142
+ };
143
+ }
144
+ const args = [
145
+ "pages",
146
+ "deploy",
147
+ ".",
148
+ `--project-name=${deployConfig.cloudflare?.projectName || deployConfig.projectName || "farm-app"}`
149
+ ];
113
150
  return {
114
- command: `wrangler pages deploy . --project-name=${deployConfig.cloudflare?.projectName || deployConfig.projectName || "farm-app"}`,
115
- cwd: outputDir
151
+ command: formatCommand$1("wrangler", args),
152
+ cwd: outputDir,
153
+ executable: "wrangler",
154
+ args
116
155
  };
117
156
  }
157
+ function createNetlifyDeployArgs(site) {
158
+ return [
159
+ "deploy",
160
+ "--prod",
161
+ "--dir=.",
162
+ ...site ? [`--site=${site}`] : []
163
+ ];
164
+ }
118
165
  /**
119
166
  * Deploy using platform's native CLI (user credentials)
120
167
  */
@@ -135,19 +182,20 @@ async function deployPlatform(platform, root, outputDir, deployConfig, prod) {
135
182
  async function deployVercel(root, outputDir, prod) {
136
183
  _farm_js_core.logger.info("🚀 Deploying to Vercel...");
137
184
  try {
138
- (0, child_process.execSync)("vercel --version", { stdio: "ignore" });
139
- } catch {
140
- _farm_js_core.logger.error("❌ Vercel CLI is not installed.");
141
- _farm_js_core.logger.info("💡 Install it with: npm i -g vercel");
142
- process.exit(1);
185
+ (0, child_process.execFileSync)("vercel", ["--version"], {
186
+ stdio: "ignore",
187
+ cwd: root
188
+ });
189
+ } catch (error) {
190
+ throw new FarmDeployError("CLI_NOT_INSTALLED", "vercel", "Vercel CLI is not installed. Install it with: npm i -g vercel", { cause: error });
143
191
  }
144
192
  try {
145
- (0, child_process.execSync)("vercel whoami", { stdio: "ignore" });
146
- } catch {
147
- _farm_js_core.logger.warn("⚠️ Not logged in to Vercel.");
148
- _farm_js_core.logger.info("💡 Please run: vercel login");
149
- _farm_js_core.logger.info(" Then run: farm deploy --vercel");
150
- process.exit(1);
193
+ (0, child_process.execFileSync)("vercel", ["whoami"], {
194
+ stdio: "ignore",
195
+ cwd: root
196
+ });
197
+ } catch (error) {
198
+ throw new FarmDeployError("CLI_NOT_AUTHENTICATED", "vercel", "Vercel CLI is not authenticated. Run 'vercel login', then retry the deployment.", { cause: error });
151
199
  }
152
200
  try {
153
201
  const { existsSync, statSync, readdirSync } = await import("fs");
@@ -156,14 +204,8 @@ async function deployVercel(root, outputDir, prod) {
156
204
  const configFile = path.default.join(outputDir, "config.json");
157
205
  const serverIndex = path.default.join(functionsDir, "index.mjs");
158
206
  _farm_js_core.logger.info("🔍 Verifying deployment structure...");
159
- if (!existsSync(functionsDir)) {
160
- _farm_js_core.logger.error(`❌ Functions directory not found at ${functionsDir}`);
161
- process.exit(1);
162
- }
163
- if (!existsSync(serverIndex)) {
164
- _farm_js_core.logger.error(`❌ Server entry point not found at ${serverIndex}`);
165
- process.exit(1);
166
- }
207
+ if (!existsSync(functionsDir)) throw new FarmDeployError("INVALID_BUILD_OUTPUT", "vercel", `Functions directory not found at ${functionsDir}.`);
208
+ if (!existsSync(serverIndex)) throw new FarmDeployError("INVALID_BUILD_OUTPUT", "vercel", `Server entry point not found at ${serverIndex}.`);
167
209
  _farm_js_core.logger.info(`✅ Functions directory: ${functionsDir}`);
168
210
  if (!existsSync(staticDir)) _farm_js_core.logger.warn(`⚠️ Static directory not found at ${staticDir}`);
169
211
  else {
@@ -244,14 +286,19 @@ async function deployVercel(root, outputDir, prod) {
244
286
  _farm_js_core.logger.info(` Static: ${staticDir}`);
245
287
  _farm_js_core.logger.info(` Config: ${configFile}`);
246
288
  _farm_js_core.logger.info("📤 Uploading to Vercel...");
247
- (0, child_process.execSync)(`vercel deploy --prebuilt --yes${prod ? " --prod" : ""}`, {
289
+ (0, child_process.execFileSync)("vercel", [
290
+ "deploy",
291
+ "--prebuilt",
292
+ "--yes",
293
+ ...prod ? ["--prod"] : []
294
+ ], {
248
295
  stdio: "inherit",
249
296
  cwd: root
250
297
  });
251
298
  _farm_js_core.logger.success("✅ Deployed to Vercel successfully!");
252
299
  } catch (error) {
253
- _farm_js_core.logger.error(`❌ Failed to deploy to Vercel: ${error.message}`);
254
- process.exit(1);
300
+ if (error instanceof FarmDeployError) throw error;
301
+ throw new FarmDeployError("DEPLOY_FAILED", "vercel", `Failed to deploy to Vercel: ${getErrorMessage(error)}`, { cause: error });
255
302
  }
256
303
  }
257
304
  /** Deploy to a composed Worker or fall back to Cloudflare Pages. */
@@ -272,8 +319,7 @@ async function deployCloudflare(root, outputDir, projectName) {
272
319
  });
273
320
  _farm_js_core.logger.success("✅ Deployed Farm and Cloudflare Agents successfully!");
274
321
  } catch (error) {
275
- _farm_js_core.logger.error(`❌ Failed to deploy to Cloudflare: ${error.message}`);
276
- process.exit(1);
322
+ throw new FarmDeployError("DEPLOY_FAILED", "cloudflare", `Failed to deploy to Cloudflare: ${getErrorMessage(error)}`, { cause: error });
277
323
  }
278
324
  return;
279
325
  }
@@ -291,8 +337,7 @@ async function deployCloudflare(root, outputDir, projectName) {
291
337
  });
292
338
  _farm_js_core.logger.success("✅ Deployed to Cloudflare Pages successfully!");
293
339
  } catch (error) {
294
- _farm_js_core.logger.error(`❌ Failed to deploy to Cloudflare: ${error.message}`);
295
- process.exit(1);
340
+ throw new FarmDeployError("DEPLOY_FAILED", "cloudflare", `Failed to deploy to Cloudflare: ${getErrorMessage(error)}`, { cause: error });
296
341
  }
297
342
  }
298
343
  /** Read the trusted Workers deployment handoff emitted by @farm.js/cf-agent. */
@@ -344,10 +389,8 @@ function assertWranglerInstalled(root) {
344
389
  stdio: "ignore",
345
390
  cwd: root
346
391
  });
347
- } catch {
348
- _farm_js_core.logger.error(" Wrangler CLI is not installed.");
349
- _farm_js_core.logger.info("💡 Install it in this project with: npm i -D wrangler");
350
- process.exit(1);
392
+ } catch (error) {
393
+ throw new FarmDeployError("CLI_NOT_INSTALLED", "cloudflare", "Wrangler CLI is not installed. Install it in this project with: npm i -D wrangler", { cause: error });
351
394
  }
352
395
  }
353
396
  function isRecord(value) {
@@ -359,22 +402,33 @@ function isRecord(value) {
359
402
  async function deployNetlify(root, outputDir, site) {
360
403
  _farm_js_core.logger.info("🚀 Deploying to Netlify...");
361
404
  try {
362
- (0, child_process.execSync)("netlify --version", { stdio: "ignore" });
363
- } catch {
364
- _farm_js_core.logger.error("❌ Netlify CLI is not installed.");
365
- _farm_js_core.logger.info("💡 Install it with: npm i -g netlify-cli");
366
- process.exit(1);
405
+ (0, child_process.execFileSync)("netlify", ["--version"], {
406
+ stdio: "ignore",
407
+ cwd: root
408
+ });
409
+ } catch (error) {
410
+ throw new FarmDeployError("CLI_NOT_INSTALLED", "netlify", "Netlify CLI is not installed. Install it with: npm i -g netlify-cli", { cause: error });
367
411
  }
368
412
  try {
369
- process.chdir(outputDir);
370
- const siteFlag = site ? ` --site=${site}` : "";
371
- (0, child_process.execSync)(`netlify deploy --prod --dir=.${siteFlag}`, { stdio: "inherit" });
413
+ (0, child_process.execFileSync)("netlify", createNetlifyDeployArgs(site), {
414
+ stdio: "inherit",
415
+ cwd: outputDir
416
+ });
372
417
  _farm_js_core.logger.success("✅ Deployed to Netlify successfully!");
373
418
  } catch (error) {
374
- _farm_js_core.logger.error(`❌ Failed to deploy to Netlify: ${error.message}`);
375
- process.exit(1);
419
+ throw new FarmDeployError("DEPLOY_FAILED", "netlify", `Failed to deploy to Netlify: ${getErrorMessage(error)}`, { cause: error });
376
420
  }
377
421
  }
422
+ function formatCommand$1(executable, args) {
423
+ return [executable, ...args].map(formatCommandArgument).join(" ");
424
+ }
425
+ function formatCommandArgument(argument) {
426
+ if (/^[A-Za-z0-9_./:=@+-]+$/.test(argument)) return argument;
427
+ return `'${argument.replace(/'/g, `'"'"'`)}'`;
428
+ }
429
+ function getErrorMessage(error) {
430
+ return error instanceof Error ? error.message : String(error);
431
+ }
378
432
  //#endregion
379
433
  //#region src/preview-gateway.ts
380
434
  const DEFAULT_GATEWAY_URL = "https://preview.farming-labs.dev";
@@ -2072,9 +2126,8 @@ function discoverMatchingPages(config, pathname) {
2072
2126
  }
2073
2127
  const sourceDirectory = node_path.default.join(source.root, source.srcDir);
2074
2128
  if (!(0, node_fs.existsSync)(sourceDirectory)) continue;
2075
- for (const entry of (0, node_fs.readdirSync)(sourceDirectory, { withFileTypes: true })) {
2076
- if (!entry.isFile() || !(0, _farm_js_core.isProgrammaticRoutesFileName)(entry.name)) continue;
2077
- const filePath = node_path.default.join(sourceDirectory, entry.name);
2129
+ for (const filePath of walkFiles(sourceDirectory)) {
2130
+ if (!/\.(?:tsx?|jsx?)$/.test(filePath) || filePath.endsWith(".d.ts")) continue;
2078
2131
  const moduleSource = (0, node_fs.readFileSync)(filePath, "utf8");
2079
2132
  for (const pattern of (0, _farm_js_core.scanProgrammaticPagePaths)(moduleSource)) {
2080
2133
  const match = matchRoutePattern(pattern, pathname);
@@ -3153,6 +3206,7 @@ function formatError(error) {
3153
3206
  return error instanceof Error ? error.message : String(error);
3154
3207
  }
3155
3208
  //#endregion
3209
+ exports.FarmDeployError = FarmDeployError;
3156
3210
  exports.FarmGeneratedArtifactsStaleError = FarmGeneratedArtifactsStaleError;
3157
3211
  exports.addFarmIntegration = require_add_integration.addFarmIntegration;
3158
3212
  exports.buildFarm = require_build.buildFarm;