@farm.js/cli 0.1.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/LICENSE +22 -0
- package/README.md +11 -0
- package/bin/farm.js +394 -0
- package/dist/add-integration-CdVfiPjG.mjs +2528 -0
- package/dist/add-integration-CdVfiPjG.mjs.map +1 -0
- package/dist/add-integration-pp16Zr0U.js +2568 -0
- package/dist/add-integration-pp16Zr0U.js.map +1 -0
- package/dist/add-integration.js +4 -0
- package/dist/add-integration.mjs +2 -0
- package/dist/build.js +47 -0
- package/dist/build.js.map +1 -0
- package/dist/build.mjs +45 -0
- package/dist/build.mjs.map +1 -0
- package/dist/dev.js +10 -0
- package/dist/dev.js.map +1 -0
- package/dist/dev.mjs +9 -0
- package/dist/dev.mjs.map +1 -0
- package/dist/index.js +2469 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2434 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Farm.js Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
package/bin/farm.js
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Match Vite's startup path by reusing Node's on-disk compilation cache when
|
|
4
|
+
// the runtime supports it. Older Node releases keep the existing behavior.
|
|
5
|
+
try {
|
|
6
|
+
const nodeModule = require("node:module");
|
|
7
|
+
nodeModule.enableCompileCache?.();
|
|
8
|
+
setTimeout(() => {
|
|
9
|
+
try {
|
|
10
|
+
nodeModule.flushCompileCache?.();
|
|
11
|
+
} catch {}
|
|
12
|
+
}, 10_000).unref();
|
|
13
|
+
} catch {}
|
|
14
|
+
|
|
15
|
+
const { program } = require("commander");
|
|
16
|
+
const { version } = require("../package.json");
|
|
17
|
+
|
|
18
|
+
const banner = `
|
|
19
|
+
_______
|
|
20
|
+
| ___ |__ _ _ __ _ __ ___
|
|
21
|
+
| |_ /| / _\` | '__| '_ \` _ \\
|
|
22
|
+
| _ \\| | (_| | | | | | | | |
|
|
23
|
+
|_| \\_\\_|\\__,_|_| |_| |_| |_|
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
program.name("farm").description("Farm.js CLI - A modern React meta-framework").version(version);
|
|
27
|
+
program.addHelpText("beforeAll", `${banner}\n`);
|
|
28
|
+
|
|
29
|
+
function collectOption(value, previous) {
|
|
30
|
+
return [...(previous || []), value];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
program
|
|
34
|
+
.command("dev")
|
|
35
|
+
.description("Start development server")
|
|
36
|
+
.option("-p, --port <port>", "Port to run the server on", "3000")
|
|
37
|
+
.option("-r, --root <root>", "Root directory", process.cwd())
|
|
38
|
+
.option("--cron", "Run configured cron routes in-process during development")
|
|
39
|
+
.action(async (options) => {
|
|
40
|
+
try {
|
|
41
|
+
const { startDevServer } = require("../dist/dev.js");
|
|
42
|
+
const server = await startDevServer(
|
|
43
|
+
{
|
|
44
|
+
root: options.root,
|
|
45
|
+
},
|
|
46
|
+
parseInt(options.port),
|
|
47
|
+
);
|
|
48
|
+
if (options.cron) {
|
|
49
|
+
const { startFarmCronScheduler } = require("../dist/index.js");
|
|
50
|
+
const address = server.httpServer?.address();
|
|
51
|
+
const port = typeof address === "object" && address ? address.port : parseInt(options.port);
|
|
52
|
+
const scheduler = await startFarmCronScheduler({
|
|
53
|
+
root: options.root,
|
|
54
|
+
url: `http://localhost:${port}`,
|
|
55
|
+
});
|
|
56
|
+
server.__farmCronScheduler = scheduler;
|
|
57
|
+
server.httpServer?.once("close", () => scheduler.stop());
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error("Failed to start development server:", error);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
program
|
|
66
|
+
.command("build")
|
|
67
|
+
.description("Build for production")
|
|
68
|
+
.option("-r, --root <root>", "Root directory", process.cwd())
|
|
69
|
+
.option("-p, --preset <preset>", "Nitro preset (node-server, vercel, cloudflare, etc.)")
|
|
70
|
+
.action(async (options) => {
|
|
71
|
+
try {
|
|
72
|
+
const { buildFarm } = require("../dist/build.js");
|
|
73
|
+
await buildFarm({
|
|
74
|
+
root: options.root,
|
|
75
|
+
preset: options.preset,
|
|
76
|
+
});
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("Failed to build:", error);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
program
|
|
84
|
+
.command("generate")
|
|
85
|
+
.description("Generate route/API types and integration schema artifacts")
|
|
86
|
+
.option("-r, --root <root>", "Root directory", process.cwd())
|
|
87
|
+
.option("-c, --config <config>", "Path to farm config file")
|
|
88
|
+
.option(
|
|
89
|
+
"--orm <orm>",
|
|
90
|
+
"Schema target to generate (prisma, drizzle, postgres, mysql, sqlite, mongodb)",
|
|
91
|
+
)
|
|
92
|
+
.option("--dialect <dialect>", "SQL dialect for Drizzle generation (postgres, mysql, sqlite)")
|
|
93
|
+
.option("-o, --output <output>", "Custom output path")
|
|
94
|
+
.action(async (options) => {
|
|
95
|
+
try {
|
|
96
|
+
const { generateFarmArtifacts } = require("../dist/index.js");
|
|
97
|
+
await generateFarmArtifacts({
|
|
98
|
+
root: options.root,
|
|
99
|
+
configPath: options.config,
|
|
100
|
+
orm: options.orm,
|
|
101
|
+
dialect: options.dialect,
|
|
102
|
+
output: options.output,
|
|
103
|
+
});
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error("Failed to generate Farm artifacts:", error);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
program
|
|
111
|
+
.command("doctor")
|
|
112
|
+
.description("Inspect project configuration and the running Farm runtime")
|
|
113
|
+
.option("-r, --root <root>", "Root directory", process.cwd())
|
|
114
|
+
.option("-c, --config <config>", "Path to farm config file")
|
|
115
|
+
.option("-p, --port <port>", "Port of a running local app")
|
|
116
|
+
.option("--host <host>", "Host of a running local app")
|
|
117
|
+
.option("--url <url>", "Base URL of a running Farm app")
|
|
118
|
+
.option("--offline", "Inspect project files without probing a running app")
|
|
119
|
+
.option("--timeout <ms>", "Live runtime probe timeout in milliseconds", "1200")
|
|
120
|
+
.option("--json", "Print machine-readable JSON")
|
|
121
|
+
.action(async (options) => {
|
|
122
|
+
try {
|
|
123
|
+
const { formatFarmDoctorReport, runFarmDoctor } = require("../dist/index.js");
|
|
124
|
+
const timeoutMs = Number.parseInt(options.timeout, 10);
|
|
125
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
126
|
+
throw new Error("--timeout must be a positive number of milliseconds.");
|
|
127
|
+
}
|
|
128
|
+
const report = await runFarmDoctor({
|
|
129
|
+
root: options.root,
|
|
130
|
+
configPath: options.config,
|
|
131
|
+
port: options.port,
|
|
132
|
+
host: options.host,
|
|
133
|
+
url: options.url,
|
|
134
|
+
offline: options.offline,
|
|
135
|
+
timeoutMs,
|
|
136
|
+
});
|
|
137
|
+
console.log(options.json ? JSON.stringify(report, null, 2) : formatFarmDoctorReport(report));
|
|
138
|
+
if (report.health === "error") process.exitCode = 1;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error("Failed to inspect Farm app:", error);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
program
|
|
146
|
+
.command("preview")
|
|
147
|
+
.description("Create a public URL for a running local Farm app")
|
|
148
|
+
.option("-r, --root <root>", "Root directory", process.cwd())
|
|
149
|
+
.option("-c, --config <config>", "Path to farm config file")
|
|
150
|
+
.option("-p, --port <port>", "Port of the running local app")
|
|
151
|
+
.option("--host <host>", "Host of the running local app", "localhost")
|
|
152
|
+
.option("--url <url>", "Full local URL to expose")
|
|
153
|
+
.option(
|
|
154
|
+
"--gateway <url>",
|
|
155
|
+
"Advanced: override the hosted Farm Preview gateway URL",
|
|
156
|
+
process.env.FARM_PREVIEW_GATEWAY_URL,
|
|
157
|
+
)
|
|
158
|
+
.option(
|
|
159
|
+
"--provider <provider>",
|
|
160
|
+
"Advanced: preview provider to use (farm, local)",
|
|
161
|
+
process.env.FARM_PREVIEW_PROVIDER,
|
|
162
|
+
)
|
|
163
|
+
.option("--name <name>", "Readable preview URL name")
|
|
164
|
+
.option("--dry-run", "Validate target detection and print the preview plan without opening it")
|
|
165
|
+
.option("--no-probe", "Skip local reachability check when --port is provided")
|
|
166
|
+
.action(async (options) => {
|
|
167
|
+
try {
|
|
168
|
+
const { previewFarm } = require("../dist/index.js");
|
|
169
|
+
await previewFarm({
|
|
170
|
+
root: options.root,
|
|
171
|
+
configPath: options.config,
|
|
172
|
+
port: options.port,
|
|
173
|
+
host: options.host,
|
|
174
|
+
url: options.url,
|
|
175
|
+
gatewayUrl: options.gateway,
|
|
176
|
+
name: options.name,
|
|
177
|
+
dryRun: options.dryRun,
|
|
178
|
+
noProbe: options.noProbe,
|
|
179
|
+
provider: options.provider,
|
|
180
|
+
});
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error("Failed to create preview:", error);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
program
|
|
188
|
+
.command("migrate [source]")
|
|
189
|
+
.description("Run one-shot migration commands or migrate from another framework")
|
|
190
|
+
.option("-r, --root <root>", "Root directory", process.cwd())
|
|
191
|
+
.option("-c, --config <config>", "Path to farm config file")
|
|
192
|
+
.option("--command <command>", "Migration command to run; can be repeated", collectOption, [])
|
|
193
|
+
.option("--dry-run", "Print migration commands without running them")
|
|
194
|
+
.option(
|
|
195
|
+
"--write",
|
|
196
|
+
"Apply framework migration changes; framework migrations are dry-run by default",
|
|
197
|
+
)
|
|
198
|
+
.option("--force", "Overwrite existing files during framework migrations")
|
|
199
|
+
.action(async (source, options) => {
|
|
200
|
+
try {
|
|
201
|
+
const { migrateFarm } = require("../dist/index.js");
|
|
202
|
+
await migrateFarm({
|
|
203
|
+
root: options.root,
|
|
204
|
+
configPath: options.config,
|
|
205
|
+
commands: options.command,
|
|
206
|
+
dryRun: options.dryRun,
|
|
207
|
+
source,
|
|
208
|
+
write: options.write,
|
|
209
|
+
force: options.force,
|
|
210
|
+
});
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.error("Failed to run migrations:", error);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const cronCommand = program
|
|
218
|
+
.command("cron")
|
|
219
|
+
.description("Inspect and manually run framework-native cron routes");
|
|
220
|
+
|
|
221
|
+
cronCommand
|
|
222
|
+
.command("list")
|
|
223
|
+
.description("List cron routes configured in farm.config")
|
|
224
|
+
.option("-r, --root <root>", "Root directory", process.cwd())
|
|
225
|
+
.option("-c, --config <config>", "Path to farm config file")
|
|
226
|
+
.option("--json", "Print machine-readable JSON")
|
|
227
|
+
.action(async (options) => {
|
|
228
|
+
try {
|
|
229
|
+
const { formatFarmCronJobs, listFarmCronJobs } = require("../dist/index.js");
|
|
230
|
+
const jobs = await listFarmCronJobs({
|
|
231
|
+
root: options.root,
|
|
232
|
+
configPath: options.config,
|
|
233
|
+
});
|
|
234
|
+
console.log(options.json ? JSON.stringify(jobs, null, 2) : formatFarmCronJobs(jobs));
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error("Failed to list cron routes:", error);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
cronCommand
|
|
242
|
+
.command("run <name>")
|
|
243
|
+
.description("Invoke one configured cron route on a running Farm app")
|
|
244
|
+
.option("-r, --root <root>", "Root directory", process.cwd())
|
|
245
|
+
.option("-c, --config <config>", "Path to farm config file")
|
|
246
|
+
.option("-p, --port <port>", "Port of the running local app", "3000")
|
|
247
|
+
.option("--host <host>", "Host of the running local app", "localhost")
|
|
248
|
+
.option("--url <url>", "Base URL of a running Farm app")
|
|
249
|
+
.option("--secret <secret>", "Bearer secret (defaults to CRON_SECRET)")
|
|
250
|
+
.option("--json", "Print the complete invocation result as JSON")
|
|
251
|
+
.action(async (name, options) => {
|
|
252
|
+
try {
|
|
253
|
+
const { runFarmCronJob } = require("../dist/index.js");
|
|
254
|
+
const result = await runFarmCronJob(name, {
|
|
255
|
+
root: options.root,
|
|
256
|
+
configPath: options.config,
|
|
257
|
+
port: options.port,
|
|
258
|
+
host: options.host,
|
|
259
|
+
url: options.url,
|
|
260
|
+
secret: options.secret,
|
|
261
|
+
});
|
|
262
|
+
if (options.json) {
|
|
263
|
+
console.log(JSON.stringify(result, null, 2));
|
|
264
|
+
} else {
|
|
265
|
+
console.log(
|
|
266
|
+
`Cron ${result.job.name} completed with ${result.status} in ${result.durationMs}ms.`,
|
|
267
|
+
);
|
|
268
|
+
if (result.body !== null && result.body !== undefined && result.body !== "") {
|
|
269
|
+
console.log(
|
|
270
|
+
typeof result.body === "string" ? result.body : JSON.stringify(result.body, null, 2),
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error("Failed to run cron route:", error);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const addCommand = program.command("add").description("Add Farm.js components to the current app");
|
|
281
|
+
|
|
282
|
+
addCommand
|
|
283
|
+
.command("integration [provider]")
|
|
284
|
+
.alias("integrations")
|
|
285
|
+
.description("Add an official Farm.js integration to the app registry")
|
|
286
|
+
.option("-r, --root <root>", "Root directory", process.cwd())
|
|
287
|
+
.option("-k, --key <key>", "Registry key to use in appIntegrations")
|
|
288
|
+
.option("-f, --file <file>", "Path to the app integrations registry", "src/lib/integrations.ts")
|
|
289
|
+
.option("--force", "Overwrite an existing generated integration component")
|
|
290
|
+
.option("--dry-run", "Show what would be added without writing files")
|
|
291
|
+
.option("--route-file <file>", "Route file path for route-based integrations")
|
|
292
|
+
.option("--ui", "Also install the provider's shadcn-based Farm UI feature pack")
|
|
293
|
+
.option("--skip-package-json", "Do not add @farm.js/integrations to package.json")
|
|
294
|
+
.option("--skip-config", "Do not create or update farm.config")
|
|
295
|
+
.option("-l, --list", "List supported integration providers")
|
|
296
|
+
.action(async (provider, options) => {
|
|
297
|
+
try {
|
|
298
|
+
const {
|
|
299
|
+
addFarmIntegration,
|
|
300
|
+
listFarmIntegrationProviders,
|
|
301
|
+
} = require("../dist/add-integration.js");
|
|
302
|
+
|
|
303
|
+
if (options.list) {
|
|
304
|
+
for (const entry of listFarmIntegrationProviders()) {
|
|
305
|
+
console.log(`${entry.name.padEnd(13)} ${entry.description}`);
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (!provider) {
|
|
311
|
+
console.error("Please pass an integration provider, or use --list.");
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const result = await addFarmIntegration({
|
|
316
|
+
root: options.root,
|
|
317
|
+
provider,
|
|
318
|
+
key: options.key,
|
|
319
|
+
integrationsFile: options.file,
|
|
320
|
+
routeFile: options.routeFile,
|
|
321
|
+
ui: options.ui,
|
|
322
|
+
dryRun: options.dryRun,
|
|
323
|
+
force: options.force,
|
|
324
|
+
skipPackageJson: options.skipPackageJson,
|
|
325
|
+
skipConfig: options.skipConfig,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const verb = options.dryRun ? "Prepared" : "Added";
|
|
329
|
+
if (result.mode === "route") {
|
|
330
|
+
console.log(`${verb} ${result.provider} route at ${result.routePath || result.routeFile}`);
|
|
331
|
+
} else {
|
|
332
|
+
console.log(`${verb} ${result.provider} integration as appIntegrations.${result.key}`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (result.ui) {
|
|
336
|
+
console.log(`UI feature: ${result.ui.feature}`);
|
|
337
|
+
if (result.ui.components.length) {
|
|
338
|
+
console.log(`Shadcn components: ${result.ui.components.join(", ")}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (result.created.length) {
|
|
343
|
+
console.log("Created:");
|
|
344
|
+
for (const file of result.created) {
|
|
345
|
+
console.log(` ${file}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (result.updated.length) {
|
|
350
|
+
console.log("Updated:");
|
|
351
|
+
for (const file of result.updated) {
|
|
352
|
+
console.log(` ${file}`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (result.env.length) {
|
|
357
|
+
console.log("Environment:");
|
|
358
|
+
for (const key of result.env) {
|
|
359
|
+
console.log(` ${key}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (result.notes.length) {
|
|
364
|
+
console.log("Notes:");
|
|
365
|
+
for (const note of result.notes) {
|
|
366
|
+
console.log(` ${note}`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.error("Failed to add integration:", error);
|
|
371
|
+
process.exit(1);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
program
|
|
376
|
+
.command("deploy")
|
|
377
|
+
.description("Deploy to a platform from deploy.target or a platform flag")
|
|
378
|
+
.option("-r, --root <root>", "Root directory", process.cwd())
|
|
379
|
+
.option("--vercel", "Deploy to Vercel")
|
|
380
|
+
.option("--cloudflare", "Deploy to Cloudflare")
|
|
381
|
+
.option("--netlify", "Deploy to Netlify")
|
|
382
|
+
.option("--prod", "Deploy to production (Vercel: uses prebuilt output)")
|
|
383
|
+
.option("--custom", "Use your own credentials (not Farm.js managed)")
|
|
384
|
+
.action(async (options) => {
|
|
385
|
+
try {
|
|
386
|
+
const { deployFarm } = require("../dist/index.js");
|
|
387
|
+
await deployFarm(options);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.error("Failed to deploy:", error);
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
program.parse();
|