@farm.js/cli 0.1.0-beta.14 → 0.1.0-beta.16
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 +29 -0
- package/dist/index.js +587 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +585 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -23,32 +23,97 @@ let node_url = require("node:url");
|
|
|
23
23
|
* Deploy Farm.js application
|
|
24
24
|
*/
|
|
25
25
|
async function deployFarm(options = {}) {
|
|
26
|
-
const
|
|
26
|
+
const { plan, deployConfig } = await resolveFarmDeployContext(options);
|
|
27
|
+
if (options.plan) {
|
|
28
|
+
_farm_js_core.logger.info(formatFarmDeployPlan(plan));
|
|
29
|
+
return plan;
|
|
30
|
+
}
|
|
31
|
+
_farm_js_core.logger.info(`🚀 Building with ${plan.preset} preset...`);
|
|
32
|
+
await require_build.buildFarm({
|
|
33
|
+
root: plan.root,
|
|
34
|
+
preset: plan.preset
|
|
35
|
+
});
|
|
36
|
+
if (!(0, fs.existsSync)(plan.outputDir)) throw new Error(`Build output not found at ${plan.outputDir}. Please run 'farm build' first.`);
|
|
37
|
+
await deployPlatform(plan.target, plan.root, plan.outputDir, deployConfig, options.prod);
|
|
38
|
+
return plan;
|
|
39
|
+
}
|
|
40
|
+
async function createFarmDeployPlan(options = {}) {
|
|
41
|
+
return (await resolveFarmDeployContext(options)).plan;
|
|
42
|
+
}
|
|
43
|
+
function formatFarmDeployPlan(plan) {
|
|
44
|
+
return [
|
|
45
|
+
"FARM / DEPLOY PLAN",
|
|
46
|
+
"",
|
|
47
|
+
`Target: ${plan.target}`,
|
|
48
|
+
`Preset: ${plan.preset}`,
|
|
49
|
+
`Runtime: ${plan.runtime}`,
|
|
50
|
+
`Output: ${plan.outputDir}`,
|
|
51
|
+
`Production: ${plan.production ? "yes" : "no"}`,
|
|
52
|
+
"",
|
|
53
|
+
`1. ${plan.build.command}`,
|
|
54
|
+
` cwd: ${plan.build.cwd}`,
|
|
55
|
+
`2. ${plan.deploy.command}`,
|
|
56
|
+
` cwd: ${plan.deploy.cwd}`,
|
|
57
|
+
...plan.cloudflareAgent ? ["", `Cloudflare Agent config: ${plan.cloudflareAgent.configPath}${plan.cloudflareAgent.generated ? " (generated during build)" : ""}`] : []
|
|
58
|
+
].join("\n");
|
|
59
|
+
}
|
|
60
|
+
async function resolveFarmDeployContext(options) {
|
|
61
|
+
const root = path.default.resolve(options.root || process.cwd());
|
|
27
62
|
const mode = "production";
|
|
28
63
|
const userConfig = await (0, _farm_js_core.loadConfig)(root, void 0, mode);
|
|
29
|
-
const config =
|
|
64
|
+
const config = await (0, _farm_js_core.resolveConfig)({
|
|
65
|
+
root,
|
|
66
|
+
...userConfig
|
|
67
|
+
}, mode);
|
|
30
68
|
const cliTarget = options.vercel ? "vercel" : options.cloudflare ? "cloudflare" : options.netlify ? "netlify" : void 0;
|
|
31
|
-
const platform = (0, _farm_js_core.normalizeDeployTarget)(cliTarget || config
|
|
32
|
-
if (platform !== "vercel" && platform !== "cloudflare" && platform !== "netlify")
|
|
33
|
-
_farm_js_core.logger.error("Please specify a deployment target with --vercel, --cloudflare, --netlify, or farm.config deploy.target.");
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
69
|
+
const platform = (0, _farm_js_core.normalizeDeployTarget)(cliTarget || config.deploy.target);
|
|
70
|
+
if (platform !== "vercel" && platform !== "cloudflare" && platform !== "netlify") throw new Error("Please specify a deployment target with --vercel, --cloudflare, --netlify, or farm.config deploy.target.");
|
|
36
71
|
const deployConfig = (0, _farm_js_core.resolveDeployConfig)(userConfig || {}, {
|
|
37
72
|
target: platform,
|
|
38
73
|
preset: cliTarget ? userConfig?.deploy?.preset || userConfig?.preset || (0, _farm_js_core.getPresetForDeployTarget)(platform) : void 0
|
|
39
74
|
});
|
|
40
75
|
const preset = deployConfig.preset || (0, _farm_js_core.getPresetForDeployTarget)(platform) || "node-server";
|
|
41
|
-
_farm_js_core.logger.info(`🚀 Building with ${preset} preset...`);
|
|
42
|
-
await require_build.buildFarm({
|
|
43
|
-
root,
|
|
44
|
-
preset
|
|
45
|
-
});
|
|
46
76
|
const nitroOutput = (0, _farm_js_core.resolveDeployOutputPath)(root, deployConfig.outputDir);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
77
|
+
const cloudflareAgent = platform === "cloudflare" ? resolveCloudflareAgentDeployPlan(root) || resolveConfiguredCloudflareAgentDeployPlan(root, config.integrations) : void 0;
|
|
78
|
+
const deploy = createDeployCommand(platform, root, nitroOutput, deployConfig, options.prod, cloudflareAgent);
|
|
79
|
+
return {
|
|
80
|
+
plan: {
|
|
81
|
+
root: path.default.resolve(root),
|
|
82
|
+
target: platform,
|
|
83
|
+
preset,
|
|
84
|
+
runtime: (0, _farm_js_core.getFarmPresetRuntime)(preset),
|
|
85
|
+
outputDir: nitroOutput,
|
|
86
|
+
production: platform === "netlify" || Boolean(options.prod),
|
|
87
|
+
build: {
|
|
88
|
+
command: `farm build --preset ${preset}`,
|
|
89
|
+
cwd: path.default.resolve(root)
|
|
90
|
+
},
|
|
91
|
+
deploy,
|
|
92
|
+
...cloudflareAgent ? { cloudflareAgent } : {}
|
|
93
|
+
},
|
|
94
|
+
deployConfig
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
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
|
+
};
|
|
102
|
+
if (platform === "netlify") {
|
|
103
|
+
const site = deployConfig.netlify?.site;
|
|
104
|
+
return {
|
|
105
|
+
command: `netlify deploy --prod --dir=.${site ? ` --site=${site}` : ""}`,
|
|
106
|
+
cwd: outputDir
|
|
107
|
+
};
|
|
50
108
|
}
|
|
51
|
-
|
|
109
|
+
if (cloudflareAgent) return {
|
|
110
|
+
command: `wrangler deploy --config ${cloudflareAgent.configPath}${cloudflareAgent.environment ? ` --env ${cloudflareAgent.environment}` : ""}`,
|
|
111
|
+
cwd: path.default.resolve(root)
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
command: `wrangler pages deploy . --project-name=${deployConfig.cloudflare?.projectName || deployConfig.projectName || "farm-app"}`,
|
|
115
|
+
cwd: outputDir
|
|
116
|
+
};
|
|
52
117
|
}
|
|
53
118
|
/**
|
|
54
119
|
* Deploy using platform's native CLI (user credentials)
|
|
@@ -253,6 +318,26 @@ function resolveCloudflareAgentDeployPlan(root) {
|
|
|
253
318
|
...typeof environment === "string" ? { environment: environment.trim() } : {}
|
|
254
319
|
};
|
|
255
320
|
}
|
|
321
|
+
function resolveConfiguredCloudflareAgentDeployPlan(root, integrations) {
|
|
322
|
+
const integration = Object.values(integrations || {}).find((value) => isRecord(value) && value.category === "agent" && value.type === "cloudflare" && value.serverRuntime === false);
|
|
323
|
+
if (!isRecord(integration) || !isRecord(integration.instance)) return void 0;
|
|
324
|
+
const configuredPath = integration.instance.config;
|
|
325
|
+
if (typeof configuredPath !== "string" || !configuredPath.trim()) return void 0;
|
|
326
|
+
const projectRoot = path.default.resolve(root);
|
|
327
|
+
const sourceConfigPath = path.default.resolve(projectRoot, configuredPath);
|
|
328
|
+
assertPathInsideProject(projectRoot, sourceConfigPath, "Cloudflare Agents source config");
|
|
329
|
+
const configPath = path.default.join(path.default.dirname(sourceConfigPath), ".farm-cf-agent.wrangler.jsonc");
|
|
330
|
+
const environment = integration.instance.environment;
|
|
331
|
+
return {
|
|
332
|
+
configPath,
|
|
333
|
+
...typeof environment === "string" && environment.trim() ? { environment: environment.trim() } : {},
|
|
334
|
+
generated: true
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function assertPathInsideProject(projectRoot, candidate, label) {
|
|
338
|
+
const relativePath = path.default.relative(projectRoot, candidate);
|
|
339
|
+
if (relativePath === ".." || relativePath.startsWith(`..${path.default.sep}`) || path.default.isAbsolute(relativePath)) throw new Error(`${label} must stay inside the Farm project root.`);
|
|
340
|
+
}
|
|
256
341
|
function assertWranglerInstalled(root) {
|
|
257
342
|
try {
|
|
258
343
|
(0, child_process.execFileSync)("wrangler", ["--version"], {
|
|
@@ -801,10 +886,20 @@ function normalizePreviewDomain(value) {
|
|
|
801
886
|
}
|
|
802
887
|
//#endregion
|
|
803
888
|
//#region src/generate.ts
|
|
889
|
+
var FarmGeneratedArtifactsStaleError = class extends Error {
|
|
890
|
+
constructor(root, stalePaths) {
|
|
891
|
+
const normalizedPaths = [...new Set(stalePaths)].sort();
|
|
892
|
+
const relativePaths = normalizedPaths.map((filePath) => node_path.default.relative(root, filePath));
|
|
893
|
+
super(`Generated types are stale:\n${relativePaths.map((filePath) => ` - ${filePath}`).join("\n")}\nRun farm generate and commit the updated files.`);
|
|
894
|
+
this.name = "FarmGeneratedArtifactsStaleError";
|
|
895
|
+
this.stalePaths = normalizedPaths;
|
|
896
|
+
}
|
|
897
|
+
};
|
|
804
898
|
const PRISMA_GENERATED_START = "// Farm.js integrations generated schema: start";
|
|
805
899
|
const PRISMA_GENERATED_END = "// Farm.js integrations generated schema: end";
|
|
806
900
|
async function generateFarmArtifacts(options = {}) {
|
|
807
901
|
const root = node_path.default.resolve(options.root || process.cwd());
|
|
902
|
+
if (options.check && hasSchemaOptions(options)) throw new Error("--check verifies generated framework types and cannot be combined with schema output options.");
|
|
808
903
|
const userConfig = await (0, _farm_js_core.loadConfig)(root, options.configPath, "development");
|
|
809
904
|
if (!userConfig && hasSchemaOptions(options)) throw new Error("No Farm config found. Please create farm.config.ts or config.ts.");
|
|
810
905
|
const resolvedConfig = await (0, _farm_js_core.resolveConfig)({
|
|
@@ -819,14 +914,20 @@ async function generateFarmArtifacts(options = {}) {
|
|
|
819
914
|
layers: resolvedConfig.layers,
|
|
820
915
|
extraRoutes,
|
|
821
916
|
suppressLintOnLink: resolvedConfig.suppressLintOnLink,
|
|
822
|
-
i18nConfig: resolvedConfig.i18n
|
|
917
|
+
i18nConfig: resolvedConfig.i18n,
|
|
918
|
+
check: options.check
|
|
823
919
|
});
|
|
920
|
+
if (options.check) {
|
|
921
|
+
if (typeArtifacts.stalePaths.length) throw new FarmGeneratedArtifactsStaleError(root, typeArtifacts.stalePaths);
|
|
922
|
+
_farm_js_core.logger.success(`Generated route, API, env${resolvedConfig.i18n.enabled ? ", and i18n" : ""} types are up to date.`);
|
|
923
|
+
return typeArtifacts;
|
|
924
|
+
}
|
|
824
925
|
_farm_js_core.logger.success(`Generated route, API, env${resolvedConfig.i18n.enabled ? ", and i18n" : ""} types (${typeArtifacts.apiRoutes.length} API route${typeArtifacts.apiRoutes.length === 1 ? "" : "s"}).`);
|
|
825
926
|
const schemas = (0, _farm_js_core.getIntegrationSchemas)(resolvedConfig.integrations);
|
|
826
927
|
const schemaEntries = Object.entries(schemas);
|
|
827
928
|
if (!schemaEntries.length) {
|
|
828
929
|
if (hasSchemaOptions(options)) _farm_js_core.logger.warn("No integration schemas were found in the current Farm config.");
|
|
829
|
-
return;
|
|
930
|
+
return typeArtifacts;
|
|
830
931
|
}
|
|
831
932
|
const packageManifest = await readPackageManifest(root);
|
|
832
933
|
const schemaOptionsExplicit = hasSchemaOptions(options);
|
|
@@ -836,12 +937,12 @@ async function generateFarmArtifacts(options = {}) {
|
|
|
836
937
|
} catch (error) {
|
|
837
938
|
if (schemaOptionsExplicit) throw error;
|
|
838
939
|
_farm_js_core.logger.warn(`Integration schemas were found, but Farm could not choose a schema target automatically: ${error.message}`);
|
|
839
|
-
return;
|
|
940
|
+
return typeArtifacts;
|
|
840
941
|
}
|
|
841
942
|
if (!orm) {
|
|
842
943
|
if (!schemaOptionsExplicit) {
|
|
843
944
|
_farm_js_core.logger.warn("Integration schemas were found, but no data layer was detected. Pass --orm prisma|drizzle|postgres|mysql|sqlite|mongodb to generate schema artifacts.");
|
|
844
|
-
return;
|
|
945
|
+
return typeArtifacts;
|
|
845
946
|
}
|
|
846
947
|
throw new Error("Could not auto-detect a schema target. Pass one explicitly with --orm prisma|drizzle|postgres|mysql|sqlite|mongodb.");
|
|
847
948
|
}
|
|
@@ -852,7 +953,7 @@ async function generateFarmArtifacts(options = {}) {
|
|
|
852
953
|
if (!(0, node_fs.existsSync)(schemaPath)) throw new Error(`Prisma target was selected but no schema file was found at ${schemaPath}. Create prisma/schema.prisma or pass --output.`);
|
|
853
954
|
await writePrismaSchema(schemaPath, collectedModels);
|
|
854
955
|
_farm_js_core.logger.success(`Generated Prisma integration schema in ${node_path.default.relative(root, schemaPath)}.`);
|
|
855
|
-
return;
|
|
956
|
+
return typeArtifacts;
|
|
856
957
|
}
|
|
857
958
|
case "drizzle": {
|
|
858
959
|
const dialect = options.dialect ?? await detectDrizzleDialect(root, packageManifest) ?? void 0;
|
|
@@ -860,7 +961,7 @@ async function generateFarmArtifacts(options = {}) {
|
|
|
860
961
|
const outputPath = options.output ? node_path.default.resolve(root, options.output) : node_path.default.join(root, "farm-integrations.generated.ts");
|
|
861
962
|
await writeGeneratedFile(outputPath, generateDrizzleSchema(collectedModels, dialect));
|
|
862
963
|
_farm_js_core.logger.success(`Generated Drizzle integration schema in ${node_path.default.relative(root, outputPath)}.`);
|
|
863
|
-
return;
|
|
964
|
+
return typeArtifacts;
|
|
864
965
|
}
|
|
865
966
|
case "postgres":
|
|
866
967
|
case "mysql":
|
|
@@ -868,13 +969,13 @@ async function generateFarmArtifacts(options = {}) {
|
|
|
868
969
|
const outputPath = options.output ? node_path.default.resolve(root, options.output) : node_path.default.join(root, `farm-integrations.generated.${orm}.sql`);
|
|
869
970
|
await writeGeneratedFile(outputPath, generateSqlSchema(collectedModels, orm));
|
|
870
971
|
_farm_js_core.logger.success(`Generated ${orm} integration schema in ${node_path.default.relative(root, outputPath)}.`);
|
|
871
|
-
return;
|
|
972
|
+
return typeArtifacts;
|
|
872
973
|
}
|
|
873
974
|
case "mongodb": {
|
|
874
975
|
const outputPath = options.output ? node_path.default.resolve(root, options.output) : node_path.default.join(root, "farm-integrations.generated.mongodb.ts");
|
|
875
976
|
await writeGeneratedFile(outputPath, generateMongoBootstrap(collectedModels));
|
|
876
977
|
_farm_js_core.logger.success(`Generated MongoDB integration bootstrap in ${node_path.default.relative(root, outputPath)}.`);
|
|
877
|
-
return;
|
|
978
|
+
return typeArtifacts;
|
|
878
979
|
}
|
|
879
980
|
}
|
|
880
981
|
}
|
|
@@ -1059,7 +1160,7 @@ function cloneSchemaModel(model) {
|
|
|
1059
1160
|
async function writePrismaSchema(schemaPath, models) {
|
|
1060
1161
|
const source = await (0, node_fs_promises.readFile)(schemaPath, "utf8");
|
|
1061
1162
|
const generated = createPrismaGeneratedBlock(generatePrismaSchema(models));
|
|
1062
|
-
const pattern = new RegExp(`${escapeRegExp(PRISMA_GENERATED_START)}[\\s\\S]*?${escapeRegExp(PRISMA_GENERATED_END)}`, "m");
|
|
1163
|
+
const pattern = new RegExp(`${escapeRegExp$1(PRISMA_GENERATED_START)}[\\s\\S]*?${escapeRegExp$1(PRISMA_GENERATED_END)}`, "m");
|
|
1063
1164
|
const nextSource = pattern.test(source) ? source.replace(pattern, generated) : `${source.trimEnd()}\n\n${generated}\n`;
|
|
1064
1165
|
await (0, node_fs_promises.writeFile)(schemaPath, nextSource, "utf8");
|
|
1065
1166
|
}
|
|
@@ -1387,12 +1488,12 @@ function toCamelCase(value) {
|
|
|
1387
1488
|
function escapeString(value) {
|
|
1388
1489
|
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/'/g, "''");
|
|
1389
1490
|
}
|
|
1390
|
-
function escapeRegExp(value) {
|
|
1491
|
+
function escapeRegExp$1(value) {
|
|
1391
1492
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1392
1493
|
}
|
|
1393
1494
|
//#endregion
|
|
1394
1495
|
//#region src/doctor.ts
|
|
1395
|
-
const ROUTE_EXTENSIONS = [
|
|
1496
|
+
const ROUTE_EXTENSIONS$1 = [
|
|
1396
1497
|
"ts",
|
|
1397
1498
|
"tsx",
|
|
1398
1499
|
"js",
|
|
@@ -1414,7 +1515,7 @@ async function runFarmDoctor(options = {}) {
|
|
|
1414
1515
|
const root = node_path.default.resolve(options.root || process.cwd());
|
|
1415
1516
|
const liveTarget = resolveLiveTarget(options);
|
|
1416
1517
|
let liveError;
|
|
1417
|
-
if (!options.offline) try {
|
|
1518
|
+
if (!options.offline && !options.fix) try {
|
|
1418
1519
|
return createLiveReport(await fetchLiveSnapshot(liveTarget, options), liveTarget, options.now);
|
|
1419
1520
|
} catch (error) {
|
|
1420
1521
|
liveError = formatError$2(error);
|
|
@@ -1461,6 +1562,13 @@ function formatFarmDoctorReport(report, options = {}) {
|
|
|
1461
1562
|
`${report.summary.fail} failed`,
|
|
1462
1563
|
`${report.summary.info} info`
|
|
1463
1564
|
].join(" / ");
|
|
1565
|
+
if (report.fixes?.length) {
|
|
1566
|
+
lines.push("", color.bold("FIXED"));
|
|
1567
|
+
for (const fix of report.fixes) {
|
|
1568
|
+
lines.push(` ${color.green("✓")} ${fix.title}`);
|
|
1569
|
+
lines.push(` ${color.dim(fix.filePath)}`);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1464
1572
|
lines.push("", `${color.bold("SUMMARY")} ${summary}`);
|
|
1465
1573
|
if (report.target?.devtoolsUrl) lines.push(`${color.bold("DEVTOOLS")} ${report.target.devtoolsUrl}`);
|
|
1466
1574
|
return lines.join("\n");
|
|
@@ -1560,9 +1668,10 @@ async function createProjectReport(root, options) {
|
|
|
1560
1668
|
action: "Add farm.config.ts and export defineConfig({...})."
|
|
1561
1669
|
});
|
|
1562
1670
|
else {
|
|
1671
|
+
const configRoot = node_path.default.resolve(root, userConfig.root || ".");
|
|
1563
1672
|
config = await (0, _farm_js_core.resolveConfig)({
|
|
1564
|
-
|
|
1565
|
-
|
|
1673
|
+
...userConfig,
|
|
1674
|
+
root: configRoot
|
|
1566
1675
|
}, "development");
|
|
1567
1676
|
const configFile = findConfigFile(root, options.configPath);
|
|
1568
1677
|
checks.push({
|
|
@@ -1586,9 +1695,40 @@ async function createProjectReport(root, options) {
|
|
|
1586
1695
|
report.target = collectDeploymentChecks(config, userConfig, checks);
|
|
1587
1696
|
collectCronChecks(config, options.env || process.env, checks);
|
|
1588
1697
|
}
|
|
1698
|
+
if (config && options.fix) {
|
|
1699
|
+
const fixes = applySafeProjectFixes(root, config, checks);
|
|
1700
|
+
if (fixes.length) {
|
|
1701
|
+
const refreshed = await createProjectReport(root, {
|
|
1702
|
+
...options,
|
|
1703
|
+
fix: false
|
|
1704
|
+
});
|
|
1705
|
+
refreshed.fixes = fixes;
|
|
1706
|
+
return refreshed;
|
|
1707
|
+
}
|
|
1708
|
+
report.fixes = [];
|
|
1709
|
+
}
|
|
1589
1710
|
finalizeReport(report);
|
|
1590
1711
|
return report;
|
|
1591
1712
|
}
|
|
1713
|
+
function applySafeProjectFixes(root, config, checks) {
|
|
1714
|
+
const fixes = [];
|
|
1715
|
+
if (checks.some((check) => check.code === "ROOT_LAYOUT_MISSING")) {
|
|
1716
|
+
const layoutPath = node_path.default.join(config.root, config.srcDir, "app", "layout.tsx");
|
|
1717
|
+
if (!(0, node_fs.existsSync)(layoutPath)) {
|
|
1718
|
+
(0, node_fs.mkdirSync)(node_path.default.dirname(layoutPath), { recursive: true });
|
|
1719
|
+
(0, node_fs.writeFileSync)(layoutPath, `import type { ReactNode } from "react";\n\nexport default function RootLayout({ children }: { children: ReactNode }) {\n return (\n <html lang="en">\n <body>{children}</body>\n </html>\n );\n}\n`, {
|
|
1720
|
+
encoding: "utf8",
|
|
1721
|
+
flag: "wx"
|
|
1722
|
+
});
|
|
1723
|
+
fixes.push({
|
|
1724
|
+
code: "ROOT_LAYOUT_CREATED",
|
|
1725
|
+
title: "Created the missing root layout",
|
|
1726
|
+
filePath: node_path.default.relative(root, layoutPath)
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
return fixes;
|
|
1731
|
+
}
|
|
1592
1732
|
function collectNodeCheck(checks) {
|
|
1593
1733
|
const major = Number(process.versions.node.split(".")[0]);
|
|
1594
1734
|
checks.push(major >= 18 ? {
|
|
@@ -1649,7 +1789,7 @@ function collectRouterChecks(config, checks) {
|
|
|
1649
1789
|
const sources = (0, _farm_js_core.getFarmSourceRoots)(config);
|
|
1650
1790
|
const appDirectories = sources.map((source) => node_path.default.join(source.root, source.srcDir, "app"));
|
|
1651
1791
|
const hasPages = appDirectories.some((directory) => containsFile(directory, /^page\.(?:ts|tsx|js|jsx|md|mdx)$/));
|
|
1652
|
-
const hasProgrammaticRoutes = sources.some((source) => ROUTE_EXTENSIONS.some((extension) => (0, node_fs.existsSync)(node_path.default.join(source.root, source.srcDir, `farm.routes.${extension}`))));
|
|
1792
|
+
const hasProgrammaticRoutes = sources.some((source) => ROUTE_EXTENSIONS$1.some((extension) => (0, node_fs.existsSync)(node_path.default.join(source.root, source.srcDir, `farm.routes.${extension}`))));
|
|
1653
1793
|
checks.push(hasPages || hasProgrammaticRoutes ? {
|
|
1654
1794
|
status: "pass",
|
|
1655
1795
|
code: "APP_ROUTER_READY",
|
|
@@ -1662,7 +1802,7 @@ function collectRouterChecks(config, checks) {
|
|
|
1662
1802
|
message: `Farm found no page modules under ${config.srcDir}/app.`,
|
|
1663
1803
|
action: `Add ${config.srcDir}/app/page.tsx or ${config.srcDir}/farm.routes.tsx.`
|
|
1664
1804
|
});
|
|
1665
|
-
const hasRootLayout = appDirectories.some((directory) => ROUTE_EXTENSIONS.some((extension) => (0, node_fs.existsSync)(node_path.default.join(directory, `layout.${extension}`))));
|
|
1805
|
+
const hasRootLayout = appDirectories.some((directory) => ROUTE_EXTENSIONS$1.some((extension) => (0, node_fs.existsSync)(node_path.default.join(directory, `layout.${extension}`))));
|
|
1666
1806
|
checks.push(hasRootLayout ? {
|
|
1667
1807
|
status: "pass",
|
|
1668
1808
|
code: "ROOT_LAYOUT_READY",
|
|
@@ -1732,7 +1872,7 @@ function hasCronRoute(config, job) {
|
|
|
1732
1872
|
const relative = job.path.replace(/^\/+/, "").replace(/^api\//, "");
|
|
1733
1873
|
return (0, _farm_js_core.getFarmSourceRoots)(config).some((source) => {
|
|
1734
1874
|
const directory = node_path.default.join(source.root, source.srcDir, "app", "api", relative);
|
|
1735
|
-
return ROUTE_EXTENSIONS.some((extension) => (0, node_fs.existsSync)(node_path.default.join(directory, `route.${extension}`)));
|
|
1875
|
+
return ROUTE_EXTENSIONS$1.some((extension) => (0, node_fs.existsSync)(node_path.default.join(directory, `route.${extension}`)));
|
|
1736
1876
|
});
|
|
1737
1877
|
}
|
|
1738
1878
|
function containsFile(directory, pattern) {
|
|
@@ -1800,6 +1940,415 @@ function formatCount$1(value, noun) {
|
|
|
1800
1940
|
return `${value} ${noun}${value === 1 ? "" : "s"}`;
|
|
1801
1941
|
}
|
|
1802
1942
|
//#endregion
|
|
1943
|
+
//#region src/explain.ts
|
|
1944
|
+
const ROUTE_EXTENSIONS = [
|
|
1945
|
+
"tsx",
|
|
1946
|
+
"ts",
|
|
1947
|
+
"jsx",
|
|
1948
|
+
"js",
|
|
1949
|
+
"mdx",
|
|
1950
|
+
"md"
|
|
1951
|
+
];
|
|
1952
|
+
const MIDDLEWARE_EXTENSIONS = [
|
|
1953
|
+
"ts",
|
|
1954
|
+
"tsx",
|
|
1955
|
+
"js",
|
|
1956
|
+
"jsx",
|
|
1957
|
+
"mjs",
|
|
1958
|
+
"cjs"
|
|
1959
|
+
];
|
|
1960
|
+
const SOCIAL_IMAGE_EXTENSIONS = [
|
|
1961
|
+
"tsx",
|
|
1962
|
+
"ts",
|
|
1963
|
+
"jsx",
|
|
1964
|
+
"js",
|
|
1965
|
+
"png",
|
|
1966
|
+
"jpg",
|
|
1967
|
+
"jpeg",
|
|
1968
|
+
"gif",
|
|
1969
|
+
"webp"
|
|
1970
|
+
];
|
|
1971
|
+
async function explainFarmRoute(pathname, options = {}) {
|
|
1972
|
+
const root = node_path.default.resolve(options.root || process.cwd());
|
|
1973
|
+
const userConfig = await (0, _farm_js_core.loadConfig)(root, options.configPath, "production");
|
|
1974
|
+
const config = await (0, _farm_js_core.resolveConfig)({
|
|
1975
|
+
root,
|
|
1976
|
+
...userConfig
|
|
1977
|
+
}, "production");
|
|
1978
|
+
const normalizedPathname = normalizePathname(pathname, config.basePath || "/");
|
|
1979
|
+
const page = discoverMatchingPages(config, normalizedPathname).sort((left, right) => right.score - left.score || right.priority - left.priority)[0];
|
|
1980
|
+
if (!page) throw new Error(`No Farm page route matches ${normalizedPathname}.`);
|
|
1981
|
+
const layouts = collectInheritedRouteFiles(config, normalizedPathname, "layout", ROUTE_EXTENSIONS);
|
|
1982
|
+
const middleware = collectMiddleware(root, Boolean(userConfig?.middleware && Object.keys(userConfig.middleware).length), config, normalizedPathname);
|
|
1983
|
+
const pageSource = (0, node_fs.readFileSync)(page.filePath, "utf8");
|
|
1984
|
+
const layoutSources = layouts.map((filePath) => ({
|
|
1985
|
+
filePath,
|
|
1986
|
+
source: (0, node_fs.readFileSync)(filePath, "utf8")
|
|
1987
|
+
}));
|
|
1988
|
+
const runtime = (0, _farm_js_core.resolveFarmRouteRuntimeConfig)((0, _farm_js_core.mergeFarmRouteRuntimeConfigs)((0, _farm_js_core.resolveFarmRouteRuleRuntimeConfig)(normalizedPathname, config.routeRules), ...layoutSources.map(({ source }) => readRuntimeExports(source)), readRuntimeExports(pageSource)), `Route ${page.pattern}`);
|
|
1989
|
+
const matchingRules = Object.entries(config.routeRules).filter(([pattern]) => (0, _farm_js_core.farmRouteRuleMatches)(pattern, normalizedPathname)).sort(([left], [right]) => routeSpecificity(left) - routeSpecificity(right));
|
|
1990
|
+
const rendering = resolveRendering(pageSource, matchingRules);
|
|
1991
|
+
const cache = resolveCaching(pageSource, matchingRules);
|
|
1992
|
+
const metadataSources = [...layoutSources, {
|
|
1993
|
+
filePath: page.filePath,
|
|
1994
|
+
source: pageSource
|
|
1995
|
+
}];
|
|
1996
|
+
const openGraphImage = findNearestSocialImage(config, normalizedPathname, "opengraph-image");
|
|
1997
|
+
const twitterImage = findNearestSocialImage(config, normalizedPathname, "twitter-image");
|
|
1998
|
+
const preset = String(config.deploy.preset || config.preset || "node-server");
|
|
1999
|
+
const presetRuntime = (0, _farm_js_core.getFarmPresetRuntime)(preset);
|
|
2000
|
+
const compatible = rendering.mode === "static" || rendering.mode === "client" || runtime.runtime === "auto" || presetRuntime !== "unknown" && runtime.runtime === presetRuntime;
|
|
2001
|
+
const warnings = [];
|
|
2002
|
+
if (presetRuntime === "unknown" && runtime.runtime !== "auto") warnings.push(`Farm cannot verify the ${runtime.runtime} route requirement because the ${preset} preset runtime is unknown.`);
|
|
2003
|
+
else if (!compatible) warnings.push(`The route requires ${runtime.runtime}, but the ${preset} preset emits ${presetRuntime} functions.`);
|
|
2004
|
+
if (runtime.regions?.length && preset !== "vercel" && preset !== "vercel-edge") warnings.push(`${preset} does not map Farm per-route region hints.`);
|
|
2005
|
+
if (runtime.maxDuration && preset !== "vercel") warnings.push(`${preset} does not map Farm per-route maxDuration.`);
|
|
2006
|
+
return {
|
|
2007
|
+
pathname: normalizedPathname,
|
|
2008
|
+
pattern: page.pattern,
|
|
2009
|
+
params: page.params,
|
|
2010
|
+
filePath: toProjectPath(root, page.filePath),
|
|
2011
|
+
source: page.source,
|
|
2012
|
+
layouts: layouts.map((filePath) => toProjectPath(root, filePath)),
|
|
2013
|
+
middleware,
|
|
2014
|
+
runtime,
|
|
2015
|
+
rendering,
|
|
2016
|
+
cache,
|
|
2017
|
+
metadata: {
|
|
2018
|
+
static: metadataSources.filter(({ source }) => /export\s+const\s+metadata\b/.test(source)).map(({ filePath }) => toProjectPath(root, filePath)),
|
|
2019
|
+
dynamic: metadataSources.filter(({ source }) => /export\s+(?:async\s+)?function\s+generateMetadata\b|export\s+const\s+generateMetadata\b/.test(source)).map(({ filePath }) => toProjectPath(root, filePath)),
|
|
2020
|
+
...openGraphImage ? { openGraphImage: toProjectPath(root, openGraphImage) } : {},
|
|
2021
|
+
...twitterImage ? { twitterImage: toProjectPath(root, twitterImage) } : {}
|
|
2022
|
+
},
|
|
2023
|
+
deployment: {
|
|
2024
|
+
target: String(config.deploy.target || "node"),
|
|
2025
|
+
preset,
|
|
2026
|
+
runtime: presetRuntime,
|
|
2027
|
+
compatible,
|
|
2028
|
+
warnings
|
|
2029
|
+
}
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
function formatFarmRouteExplanation(explanation, options = {}) {
|
|
2033
|
+
const color = options.color === void 0 ? picocolors.default : picocolors.default.createColors(options.color);
|
|
2034
|
+
const lines = [
|
|
2035
|
+
color.bold("FARM / EXPLAIN"),
|
|
2036
|
+
"",
|
|
2037
|
+
`${color.bold("Path")} ${explanation.pathname}`,
|
|
2038
|
+
`${color.bold("Pattern")} ${explanation.pattern}`,
|
|
2039
|
+
`${color.bold("File")} ${explanation.filePath}`,
|
|
2040
|
+
`${color.bold("Source")} ${explanation.source}`,
|
|
2041
|
+
`${color.bold("Params")} ${formatParams(explanation.params)}`,
|
|
2042
|
+
`${color.bold("Layouts")} ${explanation.layouts.length ? explanation.layouts.join(" -> ") : "none"}`,
|
|
2043
|
+
`${color.bold("Middleware")} ${explanation.middleware.length ? explanation.middleware.map((entry) => entry.filePath).join(", ") : "none"}`,
|
|
2044
|
+
`${color.bold("Runtime")} ${formatRuntime(explanation.runtime)}`,
|
|
2045
|
+
`${color.bold("Rendering")} ${explanation.rendering.mode} (${explanation.rendering.reason})${explanation.rendering.ppr ? ", PPR" : ""}`,
|
|
2046
|
+
`${color.bold("Caching")} ${formatCaching(explanation.cache)}`,
|
|
2047
|
+
`${color.bold("Metadata")} ${formatMetadata(explanation.metadata)}`,
|
|
2048
|
+
`${color.bold("Deployment")} ${explanation.deployment.target} / ${explanation.deployment.preset} — ${explanation.deployment.compatible ? color.green("compatible") : color.red("incompatible")}`
|
|
2049
|
+
];
|
|
2050
|
+
for (const warning of explanation.deployment.warnings) lines.push(` ${color.yellow("!")} ${warning}`);
|
|
2051
|
+
return lines.join("\n");
|
|
2052
|
+
}
|
|
2053
|
+
function discoverMatchingPages(config, pathname) {
|
|
2054
|
+
const candidates = [];
|
|
2055
|
+
for (const [priority, source] of (0, _farm_js_core.getFarmSourceRoots)(config).entries()) {
|
|
2056
|
+
const appDirectory = node_path.default.join(source.root, source.srcDir, "app");
|
|
2057
|
+
if ((0, node_fs.existsSync)(appDirectory)) for (const filePath of walkFiles(appDirectory)) {
|
|
2058
|
+
if (!/^page\.(?:tsx?|jsx?|mdx?)$/.test(node_path.default.basename(filePath))) continue;
|
|
2059
|
+
const relativeDirectory = node_path.default.relative(appDirectory, node_path.default.dirname(filePath));
|
|
2060
|
+
if (relativeDirectory.split(node_path.default.sep).includes("api") || isRouteSlotDirectory(relativeDirectory)) continue;
|
|
2061
|
+
const pattern = directoryToRoutePattern(relativeDirectory);
|
|
2062
|
+
const match = matchRoutePattern(pattern, pathname);
|
|
2063
|
+
if (!match) continue;
|
|
2064
|
+
candidates.push({
|
|
2065
|
+
filePath,
|
|
2066
|
+
pattern,
|
|
2067
|
+
params: match.params,
|
|
2068
|
+
score: match.score,
|
|
2069
|
+
source: source.name,
|
|
2070
|
+
priority
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
const sourceDirectory = node_path.default.join(source.root, source.srcDir);
|
|
2074
|
+
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);
|
|
2078
|
+
const moduleSource = (0, node_fs.readFileSync)(filePath, "utf8");
|
|
2079
|
+
for (const pattern of (0, _farm_js_core.scanProgrammaticPagePaths)(moduleSource)) {
|
|
2080
|
+
const match = matchRoutePattern(pattern, pathname);
|
|
2081
|
+
if (!match) continue;
|
|
2082
|
+
candidates.push({
|
|
2083
|
+
filePath,
|
|
2084
|
+
pattern,
|
|
2085
|
+
params: match.params,
|
|
2086
|
+
score: match.score,
|
|
2087
|
+
source: source.name,
|
|
2088
|
+
priority
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
return candidates;
|
|
2094
|
+
}
|
|
2095
|
+
function isRouteSlotDirectory(relativeDirectory) {
|
|
2096
|
+
return relativeDirectory.split(node_path.default.sep).some((segment) => /^@[A-Za-z][\w-]*$/.test(segment));
|
|
2097
|
+
}
|
|
2098
|
+
function walkFiles(directory) {
|
|
2099
|
+
const files = [];
|
|
2100
|
+
const pending = [directory];
|
|
2101
|
+
while (pending.length) {
|
|
2102
|
+
const current = pending.pop();
|
|
2103
|
+
for (const entry of (0, node_fs.readdirSync)(current, { withFileTypes: true })) {
|
|
2104
|
+
const entryPath = node_path.default.join(current, entry.name);
|
|
2105
|
+
if (entry.isDirectory()) {
|
|
2106
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
2107
|
+
pending.push(entryPath);
|
|
2108
|
+
continue;
|
|
2109
|
+
}
|
|
2110
|
+
files.push(entryPath);
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
return files;
|
|
2114
|
+
}
|
|
2115
|
+
function directoryToRoutePattern(relativeDirectory) {
|
|
2116
|
+
const segments = relativeDirectory.split(node_path.default.sep).filter(Boolean).filter((segment) => !/^\(.+\)$/.test(segment) && !segment.startsWith("@")).map((segment) => segment.replace(/^\(\.{1,3}\)/, ""));
|
|
2117
|
+
return segments.length ? `/${segments.join("/")}` : "/";
|
|
2118
|
+
}
|
|
2119
|
+
function matchRoutePattern(pattern, pathname) {
|
|
2120
|
+
const patternSegments = splitPath(pattern);
|
|
2121
|
+
const pathSegments = splitPath(pathname);
|
|
2122
|
+
const params = {};
|
|
2123
|
+
let score = 0;
|
|
2124
|
+
let pathIndex = 0;
|
|
2125
|
+
for (const segment of patternSegments) {
|
|
2126
|
+
const optionalCatchAll = segment.match(/^\[\[\.\.\.(.+)\]\]$/);
|
|
2127
|
+
if (optionalCatchAll) {
|
|
2128
|
+
params[optionalCatchAll[1]] = pathSegments.slice(pathIndex);
|
|
2129
|
+
pathIndex = pathSegments.length;
|
|
2130
|
+
score += 1;
|
|
2131
|
+
continue;
|
|
2132
|
+
}
|
|
2133
|
+
const catchAll = segment.match(/^\[\.\.\.(.+)\]$/);
|
|
2134
|
+
if (catchAll) {
|
|
2135
|
+
if (pathIndex >= pathSegments.length) return null;
|
|
2136
|
+
params[catchAll[1]] = pathSegments.slice(pathIndex);
|
|
2137
|
+
pathIndex = pathSegments.length;
|
|
2138
|
+
score += 10;
|
|
2139
|
+
continue;
|
|
2140
|
+
}
|
|
2141
|
+
const dynamic = segment.match(/^\[(.+)\]$/);
|
|
2142
|
+
if (dynamic) {
|
|
2143
|
+
if (pathIndex >= pathSegments.length) return null;
|
|
2144
|
+
params[dynamic[1]] = pathSegments[pathIndex++];
|
|
2145
|
+
score += 50;
|
|
2146
|
+
continue;
|
|
2147
|
+
}
|
|
2148
|
+
if (segment !== pathSegments[pathIndex++]) return null;
|
|
2149
|
+
score += 100;
|
|
2150
|
+
}
|
|
2151
|
+
return pathIndex === pathSegments.length ? {
|
|
2152
|
+
params,
|
|
2153
|
+
score
|
|
2154
|
+
} : null;
|
|
2155
|
+
}
|
|
2156
|
+
function collectInheritedRouteFiles(config, pathname, baseName, extensions) {
|
|
2157
|
+
return collectLayeredRouteFiles(config, baseName, extensions).filter((entry) => matchesRoutePrefix(entry.pattern, pathname)).sort(compareInheritedRouteFiles).map((entry) => entry.filePath);
|
|
2158
|
+
}
|
|
2159
|
+
function collectMiddleware(root, hasConfigMiddleware, config, pathname) {
|
|
2160
|
+
const rootMiddleware = /* @__PURE__ */ new Map();
|
|
2161
|
+
for (const source of (0, _farm_js_core.getFarmSourceRoots)(config)) {
|
|
2162
|
+
const filePath = findFile(node_path.default.join(source.root, source.srcDir), "middleware", MIDDLEWARE_EXTENSIONS);
|
|
2163
|
+
if (filePath) rootMiddleware.set("root", {
|
|
2164
|
+
filePath,
|
|
2165
|
+
pattern: "/"
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2168
|
+
const files = [...rootMiddleware.values(), ...collectLayeredRouteFiles(config, "middleware", MIDDLEWARE_EXTENSIONS).filter((entry) => matchesRoutePrefix(entry.pattern, pathname))].sort(compareInheritedRouteFiles).map((entry) => entry.filePath);
|
|
2169
|
+
return [...hasConfigMiddleware ? [{
|
|
2170
|
+
source: "config",
|
|
2171
|
+
filePath: "farm.config (middleware)"
|
|
2172
|
+
}] : [], ...files.map((filePath) => ({
|
|
2173
|
+
source: "file",
|
|
2174
|
+
filePath: toProjectPath(root, filePath)
|
|
2175
|
+
}))];
|
|
2176
|
+
}
|
|
2177
|
+
function collectLayeredRouteFiles(config, baseName, extensions) {
|
|
2178
|
+
const files = /* @__PURE__ */ new Map();
|
|
2179
|
+
const filePattern = new RegExp(`^${escapeRegExp(baseName)}\\.(?:${extensions.map(escapeRegExp).join("|")})$`);
|
|
2180
|
+
for (const source of (0, _farm_js_core.getFarmSourceRoots)(config)) {
|
|
2181
|
+
const appDirectory = node_path.default.join(source.root, source.srcDir, "app");
|
|
2182
|
+
if (!(0, node_fs.existsSync)(appDirectory)) continue;
|
|
2183
|
+
for (const filePath of walkFiles(appDirectory)) {
|
|
2184
|
+
if (!filePattern.test(node_path.default.basename(filePath))) continue;
|
|
2185
|
+
const pattern = directoryToRoutePattern(node_path.default.relative(appDirectory, node_path.default.dirname(filePath)));
|
|
2186
|
+
files.set(pattern, {
|
|
2187
|
+
filePath,
|
|
2188
|
+
pattern
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
return [...files.values()];
|
|
2193
|
+
}
|
|
2194
|
+
function findNearestSocialImage(config, pathname, baseName) {
|
|
2195
|
+
return collectLayeredRouteFiles(config, baseName, SOCIAL_IMAGE_EXTENSIONS).filter((entry) => matchesRoutePrefix(entry.pattern, pathname)).sort((left, right) => compareInheritedRouteFiles(right, left))[0]?.filePath;
|
|
2196
|
+
}
|
|
2197
|
+
function compareInheritedRouteFiles(left, right) {
|
|
2198
|
+
return splitPath(left.pattern).length - splitPath(right.pattern).length;
|
|
2199
|
+
}
|
|
2200
|
+
function matchesRoutePrefix(pattern, pathname) {
|
|
2201
|
+
const patternSegments = splitPath(pattern);
|
|
2202
|
+
const pathSegments = splitPath(pathname);
|
|
2203
|
+
if (patternSegments.length > pathSegments.length) return false;
|
|
2204
|
+
return patternSegments.every((segment, index) => {
|
|
2205
|
+
if (/^\[{1,2}(?:\.\.\.)?.+\]{1,2}$/.test(segment)) return true;
|
|
2206
|
+
return segment === pathSegments[index];
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
function findFile(directory, baseName, extensions) {
|
|
2210
|
+
return extensions.map((extension) => node_path.default.join(directory, `${baseName}.${extension}`)).find(node_fs.existsSync);
|
|
2211
|
+
}
|
|
2212
|
+
function escapeRegExp(value) {
|
|
2213
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2214
|
+
}
|
|
2215
|
+
function readRuntimeExports(source) {
|
|
2216
|
+
const runtime = readStringExport(source, "runtime");
|
|
2217
|
+
const maxDuration = readNumberOrAutoExport(source, "maxDuration");
|
|
2218
|
+
const regions = readStringArrayOrAutoExport(source, "regions");
|
|
2219
|
+
return {
|
|
2220
|
+
...runtime === "auto" || runtime === "node" || runtime === "edge" ? { runtime } : {},
|
|
2221
|
+
...regions ? { regions } : {},
|
|
2222
|
+
...maxDuration !== void 0 ? { maxDuration } : {}
|
|
2223
|
+
};
|
|
2224
|
+
}
|
|
2225
|
+
function resolveRendering(pageSource, matchingRules) {
|
|
2226
|
+
const pageRendering = (0, _farm_js_core.resolveRouteRenderingConfig)({
|
|
2227
|
+
...readBooleanExport(pageSource, "ssg") !== void 0 ? { ssg: readBooleanExport(pageSource, "ssg") } : {},
|
|
2228
|
+
...readBooleanExport(pageSource, "ppr") !== void 0 ? { ppr: readBooleanExport(pageSource, "ppr") } : {},
|
|
2229
|
+
...readBooleanExport(pageSource, "experimental_ppr") !== void 0 ? { experimental_ppr: readBooleanExport(pageSource, "experimental_ppr") } : {},
|
|
2230
|
+
...readNumberOrFalseExport(pageSource, "revalidate") !== void 0 ? { revalidate: readNumberOrFalseExport(pageSource, "revalidate") } : {},
|
|
2231
|
+
...readDynamicExport(pageSource) ? { dynamic: readDynamicExport(pageSource) } : {}
|
|
2232
|
+
}, pageSource);
|
|
2233
|
+
let mode = pageRendering.ssg ? "static" : pageRendering.ppr ? "partial" : "dynamic";
|
|
2234
|
+
let reason = pageRendering.directive ? `page directive ${JSON.stringify(pageRendering.directive)}` : pageRendering.ssg ? "page static rendering declaration" : pageRendering.ppr ? "page PPR declaration" : "default server rendering";
|
|
2235
|
+
let ppr = pageRendering.ppr;
|
|
2236
|
+
for (const [pattern, rule] of matchingRules) if (rule.prerender === true || rule.render === "static") {
|
|
2237
|
+
mode = "static";
|
|
2238
|
+
reason = `routeRules ${pattern}`;
|
|
2239
|
+
ppr = false;
|
|
2240
|
+
} else if (rule.prerender === false || rule.render === "dynamic" || rule.ssr === true) {
|
|
2241
|
+
mode = "dynamic";
|
|
2242
|
+
reason = `routeRules ${pattern}`;
|
|
2243
|
+
ppr = false;
|
|
2244
|
+
} else if (rule.ssr === false) {
|
|
2245
|
+
mode = "client";
|
|
2246
|
+
reason = `routeRules ${pattern}`;
|
|
2247
|
+
ppr = false;
|
|
2248
|
+
}
|
|
2249
|
+
return {
|
|
2250
|
+
mode,
|
|
2251
|
+
reason,
|
|
2252
|
+
ppr
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
function resolveCaching(pageSource, matchingRules) {
|
|
2256
|
+
let swr;
|
|
2257
|
+
let isr;
|
|
2258
|
+
for (const [, rule] of matchingRules) {
|
|
2259
|
+
if (typeof rule.swr === "number" || typeof rule.swr === "boolean") swr = rule.swr;
|
|
2260
|
+
if (typeof rule.isr === "number" || typeof rule.isr === "boolean") isr = rule.isr;
|
|
2261
|
+
}
|
|
2262
|
+
return {
|
|
2263
|
+
...readNumberOrFalseExport(pageSource, "revalidate") !== void 0 ? { revalidate: readNumberOrFalseExport(pageSource, "revalidate") } : {},
|
|
2264
|
+
...swr !== void 0 ? { swr } : {},
|
|
2265
|
+
...isr !== void 0 ? { isr } : {},
|
|
2266
|
+
rules: matchingRules.map(([pattern]) => pattern)
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
function readStringExport(source, name) {
|
|
2270
|
+
return source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*["']([^"']+)["']`))?.[1];
|
|
2271
|
+
}
|
|
2272
|
+
function readDynamicExport(source) {
|
|
2273
|
+
const dynamic = readStringExport(source, "dynamic");
|
|
2274
|
+
return dynamic === "auto" || dynamic === "force-dynamic" || dynamic === "error" || dynamic === "force-static" ? dynamic : void 0;
|
|
2275
|
+
}
|
|
2276
|
+
function readBooleanExport(source, name) {
|
|
2277
|
+
const value = source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*(true|false)`))?.[1];
|
|
2278
|
+
return value === void 0 ? void 0 : value === "true";
|
|
2279
|
+
}
|
|
2280
|
+
function readNumberOrAutoExport(source, name) {
|
|
2281
|
+
const value = source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*(?:["'](auto)["']|(\\d+))`));
|
|
2282
|
+
return value?.[1] === "auto" ? "auto" : value?.[2] ? Number(value[2]) : void 0;
|
|
2283
|
+
}
|
|
2284
|
+
function readNumberOrFalseExport(source, name) {
|
|
2285
|
+
const value = source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*(false|\\d+)`))?.[1];
|
|
2286
|
+
return value === "false" ? false : value ? Number(value) : void 0;
|
|
2287
|
+
}
|
|
2288
|
+
function readStringArrayOrAutoExport(source, name) {
|
|
2289
|
+
if (source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*["']auto["']`))) return "auto";
|
|
2290
|
+
const array = source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*\\[([^\\]]*)\\]`))?.[1];
|
|
2291
|
+
if (array === void 0) return void 0;
|
|
2292
|
+
return [...array.matchAll(/["']([^"']+)["']/g)].map((match) => match[1]);
|
|
2293
|
+
}
|
|
2294
|
+
function routeSpecificity(pattern) {
|
|
2295
|
+
return splitPath(pattern).reduce((score, segment) => {
|
|
2296
|
+
if (segment === "**" || segment.startsWith("[[...")) return score + 1;
|
|
2297
|
+
if (segment === "*" || segment.startsWith("[...")) return score + 10;
|
|
2298
|
+
if (segment.startsWith("[") || segment.startsWith(":")) return score + 50;
|
|
2299
|
+
return score + 100;
|
|
2300
|
+
}, 0);
|
|
2301
|
+
}
|
|
2302
|
+
function normalizePathname(value, basePath) {
|
|
2303
|
+
let pathname;
|
|
2304
|
+
try {
|
|
2305
|
+
pathname = new URL(value, "http://farm.local").pathname;
|
|
2306
|
+
} catch {
|
|
2307
|
+
pathname = value;
|
|
2308
|
+
}
|
|
2309
|
+
pathname = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
2310
|
+
const normalizedBase = basePath && basePath !== "/" ? `/${basePath.replace(/^\/+|\/+$/g, "")}` : "";
|
|
2311
|
+
if (normalizedBase && (pathname === normalizedBase || pathname.startsWith(`${normalizedBase}/`))) pathname = pathname.slice(normalizedBase.length) || "/";
|
|
2312
|
+
return pathname.length > 1 ? pathname.replace(/\/+$/, "") : pathname;
|
|
2313
|
+
}
|
|
2314
|
+
function splitPath(value) {
|
|
2315
|
+
return value.split("/").filter(Boolean).map(decodeURIComponent);
|
|
2316
|
+
}
|
|
2317
|
+
function toProjectPath(root, filePath) {
|
|
2318
|
+
let relativePath = node_path.default.relative(root, filePath);
|
|
2319
|
+
if ((relativePath === ".." || relativePath.startsWith(`..${node_path.default.sep}`)) && (0, node_fs.existsSync)(root) && (0, node_fs.existsSync)(filePath)) relativePath = node_path.default.relative((0, node_fs.realpathSync)(root), (0, node_fs.realpathSync)(filePath));
|
|
2320
|
+
return relativePath.split(node_path.default.sep).join("/");
|
|
2321
|
+
}
|
|
2322
|
+
function formatParams(params) {
|
|
2323
|
+
const entries = Object.entries(params);
|
|
2324
|
+
return entries.length ? entries.map(([key, value]) => `${key}=${Array.isArray(value) ? value.join("/") : value}`).join(", ") : "none";
|
|
2325
|
+
}
|
|
2326
|
+
function formatRuntime(runtime) {
|
|
2327
|
+
return [
|
|
2328
|
+
runtime.runtime,
|
|
2329
|
+
runtime.regions?.length ? `regions=${runtime.regions.join(",")}` : "",
|
|
2330
|
+
runtime.maxDuration ? `maxDuration=${runtime.maxDuration}s` : ""
|
|
2331
|
+
].filter(Boolean).join(", ");
|
|
2332
|
+
}
|
|
2333
|
+
function formatCaching(cache) {
|
|
2334
|
+
const values = [
|
|
2335
|
+
cache.revalidate !== void 0 ? `revalidate=${cache.revalidate}` : "",
|
|
2336
|
+
cache.swr !== void 0 ? `swr=${cache.swr}` : "",
|
|
2337
|
+
cache.isr !== void 0 ? `isr=${cache.isr}` : "",
|
|
2338
|
+
cache.rules.length ? `rules=${cache.rules.join(",")}` : ""
|
|
2339
|
+
].filter(Boolean);
|
|
2340
|
+
return values.length ? values.join("; ") : "request-time / no declared cache";
|
|
2341
|
+
}
|
|
2342
|
+
function formatMetadata(metadata) {
|
|
2343
|
+
const values = [
|
|
2344
|
+
metadata.static.length ? `static=${metadata.static.join(",")}` : "",
|
|
2345
|
+
metadata.dynamic.length ? `dynamic=${metadata.dynamic.join(",")}` : "",
|
|
2346
|
+
metadata.openGraphImage ? `og=${metadata.openGraphImage}` : "",
|
|
2347
|
+
metadata.twitterImage ? `twitter=${metadata.twitterImage}` : ""
|
|
2348
|
+
].filter(Boolean);
|
|
2349
|
+
return values.length ? values.join("; ") : "none";
|
|
2350
|
+
}
|
|
2351
|
+
//#endregion
|
|
1803
2352
|
//#region src/cron.ts
|
|
1804
2353
|
async function loadFarmCronConfig(options = {}) {
|
|
1805
2354
|
const root = node_path.default.resolve(options.root || process.cwd());
|
|
@@ -2604,8 +3153,10 @@ function formatError(error) {
|
|
|
2604
3153
|
return error instanceof Error ? error.message : String(error);
|
|
2605
3154
|
}
|
|
2606
3155
|
//#endregion
|
|
3156
|
+
exports.FarmGeneratedArtifactsStaleError = FarmGeneratedArtifactsStaleError;
|
|
2607
3157
|
exports.addFarmIntegration = require_add_integration.addFarmIntegration;
|
|
2608
3158
|
exports.buildFarm = require_build.buildFarm;
|
|
3159
|
+
exports.createFarmDeployPlan = createFarmDeployPlan;
|
|
2609
3160
|
exports.createFarmUpgradePlan = createFarmUpgradePlan;
|
|
2610
3161
|
exports.createFrameworkMigrationPlan = createFrameworkMigrationPlan;
|
|
2611
3162
|
exports.createGatewaySession = createGatewaySession;
|
|
@@ -2619,8 +3170,11 @@ Object.defineProperty(exports, "createServer", {
|
|
|
2619
3170
|
});
|
|
2620
3171
|
exports.deployFarm = deployFarm;
|
|
2621
3172
|
exports.detectFarmPackageManager = detectFarmPackageManager;
|
|
3173
|
+
exports.explainFarmRoute = explainFarmRoute;
|
|
2622
3174
|
exports.formatFarmCronJobs = formatFarmCronJobs;
|
|
3175
|
+
exports.formatFarmDeployPlan = formatFarmDeployPlan;
|
|
2623
3176
|
exports.formatFarmDoctorReport = formatFarmDoctorReport;
|
|
3177
|
+
exports.formatFarmRouteExplanation = formatFarmRouteExplanation;
|
|
2624
3178
|
exports.formatFarmUpgradePlan = formatFarmUpgradePlan;
|
|
2625
3179
|
exports.forwardGatewayRequest = forwardGatewayRequest;
|
|
2626
3180
|
exports.generateFarmArtifacts = generateFarmArtifacts;
|