@farm.js/core 0.1.21 → 0.1.22

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.
Files changed (49) hide show
  1. package/dist/{app-D_6nTit-.d.mts → app-0e-oTC_R.d.mts} +7 -0
  2. package/dist/{app-DAyPE-gr.d.ts → app-B7qKw8Ny.d.ts} +7 -0
  3. package/dist/{chunk-Q2TUENW2.cjs → chunk-2HCEC2YO.cjs} +1 -1
  4. package/dist/{chunk-53G3OLTA.mjs → chunk-AAWTJW2G.mjs} +215 -53
  5. package/dist/{chunk-OLYK7EAK.mjs → chunk-DAATZTKJ.mjs} +14 -0
  6. package/dist/{chunk-RWXZKZXB.cjs → chunk-EXUMQVY3.cjs} +166 -205
  7. package/dist/{chunk-NRS46D4L.mjs → chunk-GKT4ONKN.mjs} +1 -1
  8. package/dist/{chunk-YP5JZ4LF.cjs → chunk-GXXONIAR.cjs} +3 -3
  9. package/dist/{chunk-W7MZXRXM.mjs → chunk-ONMW3EVV.mjs} +1 -1
  10. package/dist/{chunk-L7ARWZYT.cjs → chunk-P5HSJCXI.cjs} +441 -279
  11. package/dist/{chunk-5L6NBNHI.mjs → chunk-PC33YKTC.mjs} +32 -71
  12. package/dist/{chunk-TFAXUHWX.cjs → chunk-PSV6XKPO.cjs} +15 -1
  13. package/dist/{chunk-ZE6ONQ5R.cjs → chunk-Q3PX2TGL.cjs} +2 -2
  14. package/dist/{chunk-XFFQSD2F.cjs → chunk-SO7CRMP6.cjs} +2 -2
  15. package/dist/{chunk-RMIJFZU3.mjs → chunk-SPSQFO3Z.mjs} +1 -1
  16. package/dist/{chunk-PKO4FCTL.mjs → chunk-X2ZUD4GI.mjs} +1 -1
  17. package/dist/{devtools-R6MDDAZK.cjs → devtools-6DQHO7EK.cjs} +3 -3
  18. package/dist/{devtools-MTB5SA2B.mjs → devtools-EZ5ORGAK.mjs} +2 -2
  19. package/dist/docs.cjs +4 -4
  20. package/dist/docs.mjs +3 -3
  21. package/dist/index.cjs +8 -8
  22. package/dist/index.d.mts +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.mjs +7 -7
  25. package/dist/internal/build-runtime.cjs +8 -8
  26. package/dist/internal/build-runtime.mjs +7 -7
  27. package/dist/internal/cli-runtime.cjs +4 -4
  28. package/dist/internal/cli-runtime.mjs +3 -3
  29. package/dist/internal/client-error-overlay.cjs +4 -4
  30. package/dist/internal/client-error-overlay.mjs +1 -1
  31. package/dist/internal/config-runtime.cjs +3 -3
  32. package/dist/internal/config-runtime.mjs +2 -2
  33. package/dist/internal/product-telemetry-runtime.cjs +2 -2
  34. package/dist/internal/product-telemetry-runtime.mjs +1 -1
  35. package/dist/plugin.cjs +5 -5
  36. package/dist/plugin.mjs +4 -4
  37. package/dist/renderer.cjs +4 -2
  38. package/dist/renderer.d.mts +9 -1
  39. package/dist/renderer.d.ts +9 -1
  40. package/dist/renderer.mjs +3 -1
  41. package/dist/server.cjs +22 -28
  42. package/dist/server.d.mts +1 -1
  43. package/dist/server.d.ts +1 -1
  44. package/dist/server.mjs +7 -13
  45. package/dist/version.cjs +2 -2
  46. package/dist/version.mjs +1 -1
  47. package/dist/vite.cjs +5 -5
  48. package/dist/vite.mjs +4 -4
  49. package/package.json +7 -3
@@ -315,9 +315,16 @@ declare class ServerRenderer {
315
315
  private i18nRuntime?;
316
316
  private viteServer?;
317
317
  private rendererRuntime;
318
+ private appliedAppSetupModule;
318
319
  private security;
319
320
  constructor(config: Required<FarmConfig>, routeManager: RouteManager, i18nRuntime?: FarmI18nRuntime, viteServer?: ViteDevServer);
320
321
  initialize(): Promise<void>;
322
+ /**
323
+ * Pass the app's renderer setup module (`renderer.appSetup`) to the renderer. In development
324
+ * the module runner returns the cached module until the file changes, so an edit is
325
+ * re-applied on the next request without restarting.
326
+ */
327
+ private applyRendererAppSetup;
321
328
  private createPageBoundary;
322
329
  private devGlobalStylesheetHref;
323
330
  private devGlobalStylesheetLink;
@@ -315,9 +315,16 @@ declare class ServerRenderer {
315
315
  private i18nRuntime?;
316
316
  private viteServer?;
317
317
  private rendererRuntime;
318
+ private appliedAppSetupModule;
318
319
  private security;
319
320
  constructor(config: Required<FarmConfig>, routeManager: RouteManager, i18nRuntime?: FarmI18nRuntime, viteServer?: ViteDevServer);
320
321
  initialize(): Promise<void>;
322
+ /**
323
+ * Pass the app's renderer setup module (`renderer.appSetup`) to the renderer. In development
324
+ * the module runner returns the cached module until the file changes, so an edit is
325
+ * re-applied on the next request without restarting.
326
+ */
327
+ private applyRendererAppSetup;
321
328
  private createPageBoundary;
322
329
  private devGlobalStylesheetHref;
323
330
  private devGlobalStylesheetLink;
@@ -1,5 +1,5 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});// package.json
2
- var version = "0.1.21";
2
+ var version = "0.1.22";
3
3
 
4
4
  // src/version.ts
5
5
  var FARM_VERSION = version;
@@ -6,7 +6,7 @@ import {
6
6
  parseRouteSlotFile,
7
7
  resolveRouteRenderingConfig,
8
8
  resolveRouteRenderingConfigFromFile
9
- } from "./chunk-NRS46D4L.mjs";
9
+ } from "./chunk-GKT4ONKN.mjs";
10
10
  import {
11
11
  collectSchemaExtensions,
12
12
  declareSchemaTables,
@@ -86,8 +86,9 @@ import {
86
86
  readFarmRendererWebStream,
87
87
  resolveFarmComponentExtensions,
88
88
  resolveFarmRenderer,
89
+ resolveFarmRendererAppSetup,
89
90
  resolveFarmRendererModule
90
- } from "./chunk-OLYK7EAK.mjs";
91
+ } from "./chunk-DAATZTKJ.mjs";
91
92
  import {
92
93
  FARM_PREVIEW_ROBOTS_TAG,
93
94
  addFarmCspNonceToScriptTags,
@@ -264,7 +265,7 @@ import {
264
265
  } from "./chunk-Z2RZJZIZ.mjs";
265
266
  import {
266
267
  FARM_VERSION
267
- } from "./chunk-PKO4FCTL.mjs";
268
+ } from "./chunk-X2ZUD4GI.mjs";
268
269
  import {
269
270
  resolveFarmTrailingSlashRedirect,
270
271
  setFarmTrailingSlashPreference
@@ -1439,9 +1440,22 @@ function resolveImportedModule(importerPath, specifier, root) {
1439
1440
  }
1440
1441
  const aliased = resolveProjectAliasSourcePath(specifier, root);
1441
1442
  if (aliased) return { path: aliased, local: true };
1442
- return { path: resolvePackageModuleSourcePath(importerPath, specifier, root), local: false };
1443
+ const packagePath = resolvePackageModuleSourcePath(importerPath, specifier, root);
1444
+ if (!packagePath) warnUnresolvedAliasImport(importerPath, specifier, root);
1445
+ return { path: packagePath, local: false };
1443
1446
  }
1444
1447
  __name(resolveImportedModule, "resolveImportedModule");
1448
+ var warnedUnresolvedAliasImports = /* @__PURE__ */ new Set();
1449
+ var ALIAS_IMPORT = /^(?:@\/|~\/|#)/;
1450
+ function warnUnresolvedAliasImport(importerPath, specifier, root) {
1451
+ if (!ALIAS_IMPORT.test(specifier) || warnedUnresolvedAliasImports.has(specifier)) return;
1452
+ warnedUnresolvedAliasImports.add(specifier);
1453
+ const importer = root ? path.relative(root, importerPath) : importerPath;
1454
+ logger.warn(
1455
+ `Could not resolve "${specifier}" (imported by ${importer}) while checking which pages need client JavaScript. If it holds client components, they will not hydrate. Map the alias in tsconfig.json "paths" or package.json "imports".`
1456
+ );
1457
+ }
1458
+ __name(warnUnresolvedAliasImport, "warnUnresolvedAliasImport");
1445
1459
  var projectImportAliasCache = /* @__PURE__ */ new Map();
1446
1460
  var PROJECT_ALIAS_FILES = ["tsconfig.json", "jsconfig.json", "package.json"];
1447
1461
  function resolveProjectAliasSourcePath(specifier, root) {
@@ -4833,6 +4847,29 @@ var _ServerRenderer = class _ServerRenderer {
4833
4847
  );
4834
4848
  this.rendererRuntime = runtime;
4835
4849
  this.routeManager.setRendererRuntime?.(this.rendererRuntime);
4850
+ await this.applyRendererAppSetup();
4851
+ }
4852
+ /**
4853
+ * Pass the app's renderer setup module (`renderer.appSetup`) to the renderer. In development
4854
+ * the module runner returns the cached module until the file changes, so an edit is
4855
+ * re-applied on the next request without restarting.
4856
+ */
4857
+ async applyRendererAppSetup() {
4858
+ const appSetupPath = resolveFarmRendererAppSetup(
4859
+ this.config.root || process.cwd(),
4860
+ this.config.renderer
4861
+ );
4862
+ if (!appSetupPath) return;
4863
+ const configureApp = this.rendererRuntime.configureApp;
4864
+ if (typeof configureApp !== "function") {
4865
+ throw new Error(
4866
+ `Renderer \`${this.config.renderer.name}\` does not support \`appSetup\`: its server module must export configureApp().`
4867
+ );
4868
+ }
4869
+ const setupModule = this.viteServer ? await loadFarmDevModule(this.viteServer, appSetupPath) : await import(pathToFileURL2(appSetupPath).href);
4870
+ if (setupModule === this.appliedAppSetupModule) return;
4871
+ this.appliedAppSetupModule = setupModule;
4872
+ configureApp(setupModule.default);
4836
4873
  }
4837
4874
  createPageBoundary(pageElement, options) {
4838
4875
  return this.rendererRuntime.createElement(
@@ -4949,6 +4986,7 @@ var _ServerRenderer = class _ServerRenderer {
4949
4986
  */
4950
4987
  async renderNavigationFragment(input) {
4951
4988
  await this.initialize();
4989
+ if (this.viteServer) await this.applyRendererAppSetup();
4952
4990
  setFarmBasePath(this.config.basePath);
4953
4991
  setFarmTrailingSlashPreference(this.config.trailingSlash);
4954
4992
  let element = this.rendererRuntime.createElement(input.PageComponent, input.pageProps);
@@ -5274,6 +5312,7 @@ var _ServerRenderer = class _ServerRenderer {
5274
5312
  }
5275
5313
  async renderPage(req, res) {
5276
5314
  await this.initialize();
5315
+ if (this.viteServer) await this.applyRendererAppSetup();
5277
5316
  setFarmBasePath(this.config.basePath);
5278
5317
  setFarmTrailingSlashPreference(this.config.trailingSlash);
5279
5318
  const cspNonce = createFarmCspNonce(this.security);
@@ -7377,6 +7416,115 @@ function createFarmApp(config, viteServer) {
7377
7416
  }
7378
7417
  __name(createFarmApp, "createFarmApp");
7379
7418
 
7419
+ // src/build/tailwind.ts
7420
+ import path11 from "path";
7421
+
7422
+ // src/optional-peer.ts
7423
+ function isMissingPackageError(error, packageName) {
7424
+ if (!error || typeof error !== "object") return false;
7425
+ const { code, message } = error;
7426
+ if (code !== "ERR_MODULE_NOT_FOUND" && code !== "MODULE_NOT_FOUND") return false;
7427
+ if (typeof message !== "string") return false;
7428
+ return message.includes(`'${packageName}'`) || message.includes(`"${packageName}"`);
7429
+ }
7430
+ __name(isMissingPackageError, "isMissingPackageError");
7431
+ function formatOptionalPeerInstallHint(packageName, purpose, options = {}) {
7432
+ return `Install "${packageName}" to ${purpose}: pnpm add ${options.dev ? "-D " : ""}${packageName}`;
7433
+ }
7434
+ __name(formatOptionalPeerInstallHint, "formatOptionalPeerInstallHint");
7435
+
7436
+ // src/build/vite-plugin-compat.ts
7437
+ function adaptTailwindVitePlugin(pluginOption, builder) {
7438
+ if (builder !== "rolldown") return pluginOption;
7439
+ return adaptPluginOption(pluginOption);
7440
+ }
7441
+ __name(adaptTailwindVitePlugin, "adaptTailwindVitePlugin");
7442
+ function adaptPluginOption(pluginOption) {
7443
+ if (Array.isArray(pluginOption)) return pluginOption.map(adaptPluginOption);
7444
+ if (!pluginOption || typeof pluginOption !== "object") return pluginOption;
7445
+ const plugin = pluginOption;
7446
+ if (!plugin.name?.startsWith("@tailwindcss/vite:generate:")) return pluginOption;
7447
+ if (isVitePluginHook(plugin.transform)) {
7448
+ return {
7449
+ ...plugin,
7450
+ transform: withVite5TransformContext(plugin.transform)
7451
+ };
7452
+ }
7453
+ if (plugin.transform && typeof plugin.transform === "object" && "handler" in plugin.transform && isVitePluginHook(plugin.transform.handler)) {
7454
+ return {
7455
+ ...plugin,
7456
+ transform: {
7457
+ ...plugin.transform,
7458
+ handler: withVite5TransformContext(plugin.transform.handler)
7459
+ }
7460
+ };
7461
+ }
7462
+ return pluginOption;
7463
+ }
7464
+ __name(adaptPluginOption, "adaptPluginOption");
7465
+ function isVitePluginHook(value) {
7466
+ return typeof value === "function";
7467
+ }
7468
+ __name(isVitePluginHook, "isVitePluginHook");
7469
+ function withVite5TransformContext(handler) {
7470
+ return function(...args) {
7471
+ const context = new Proxy(this, {
7472
+ get(target, property, receiver) {
7473
+ if (property === "environment") return void 0;
7474
+ return Reflect.get(target, property, receiver);
7475
+ }
7476
+ });
7477
+ return handler.apply(context, args);
7478
+ };
7479
+ }
7480
+ __name(withVite5TransformContext, "withVite5TransformContext");
7481
+
7482
+ // src/build/tailwind.ts
7483
+ var TAILWIND_CSS_IMPORT = /@import\s+["']tailwindcss["']/;
7484
+ var TAILWIND_VITE_INSTALL_HINT = formatOptionalPeerInstallHint(
7485
+ "@tailwindcss/vite",
7486
+ "enable built-in Tailwind CSS support",
7487
+ { dev: true }
7488
+ );
7489
+ var warnedMissingTailwind = false;
7490
+ async function loadTailwindVitePlugin(options = {}, importTailwind = () => import("@tailwindcss/vite")) {
7491
+ try {
7492
+ const tailwindVite = (await importTailwind()).default;
7493
+ const plugin = options.builder ? adaptTailwindVitePlugin(tailwindVite(), options.builder) : tailwindVite();
7494
+ if (options.announce) logger.info("\u{1F4E6} Enabled built-in Tailwind support (@tailwindcss/vite)");
7495
+ return plugin;
7496
+ } catch (error) {
7497
+ if (isMissingPackageError(error, "@tailwindcss/vite")) return createMissingTailwindPlugin();
7498
+ logger.warn(
7499
+ `Tailwind plugin auto-enable failed${options.label ? ` ${options.label}` : ""}; continuing without it: ${error.message}`
7500
+ );
7501
+ return void 0;
7502
+ }
7503
+ }
7504
+ __name(loadTailwindVitePlugin, "loadTailwindVitePlugin");
7505
+ function createMissingTailwindPlugin() {
7506
+ let root = process.cwd();
7507
+ return {
7508
+ name: "farm:tailwind-missing",
7509
+ enforce: "pre",
7510
+ configResolved(config) {
7511
+ root = config.root;
7512
+ },
7513
+ transform(code, id) {
7514
+ if (warnedMissingTailwind || !/\.css(?:\?.*)?$/.test(id) || !TAILWIND_CSS_IMPORT.test(code)) {
7515
+ return null;
7516
+ }
7517
+ warnedMissingTailwind = true;
7518
+ const file = path11.relative(root, id.split("?", 1)[0]).replace(/\\/g, "/");
7519
+ logger.warn(
7520
+ `${file} imports Tailwind CSS, but @tailwindcss/vite is not installed; continuing without Tailwind. ${TAILWIND_VITE_INSTALL_HINT}`
7521
+ );
7522
+ return null;
7523
+ }
7524
+ };
7525
+ }
7526
+ __name(createMissingTailwindPlugin, "createMissingTailwindPlugin");
7527
+
7380
7528
  // src/schema-step-types.ts
7381
7529
  function validateSchemaSteps(owner, schema, steps) {
7382
7530
  if (steps === void 0) return;
@@ -7463,8 +7611,8 @@ __name(validateSchemaSteps, "validateSchemaSteps");
7463
7611
  var _FarmPluginOptionsError = class _FarmPluginOptionsError extends TypeError {
7464
7612
  constructor(issues) {
7465
7613
  const lines = issues.map((issue) => {
7466
- const path17 = formatIssuePath(issue.path);
7467
- return ` - ${path17 ? `${path17}: ` : ""}${issue.message}`;
7614
+ const path18 = formatIssuePath(issue.path);
7615
+ return ` - ${path18 ? `${path18}: ` : ""}${issue.message}`;
7468
7616
  });
7469
7617
  super(`Invalid plugin options:
7470
7618
  ${lines.join("\n")}`);
@@ -7474,9 +7622,9 @@ ${lines.join("\n")}`);
7474
7622
  };
7475
7623
  __name(_FarmPluginOptionsError, "FarmPluginOptionsError");
7476
7624
  var FarmPluginOptionsError = _FarmPluginOptionsError;
7477
- function formatIssuePath(path17) {
7478
- if (!path17?.length) return "";
7479
- return path17.map((segment) => {
7625
+ function formatIssuePath(path18) {
7626
+ if (!path18?.length) return "";
7627
+ return path18.map((segment) => {
7480
7628
  const key = typeof segment === "object" && segment !== null ? segment.key : segment;
7481
7629
  return typeof key === "symbol" ? key.toString() : String(key);
7482
7630
  }).join(".");
@@ -7712,23 +7860,23 @@ var _PluginManager = class _PluginManager {
7712
7860
  `Farm plugin "${plugin.name}" runtime.endpoints must contain endpoint objects`
7713
7861
  );
7714
7862
  }
7715
- const path17 = normalizeFarmPluginRuntimeEndpointPath(
7863
+ const path18 = normalizeFarmPluginRuntimeEndpointPath(
7716
7864
  endpoint.path,
7717
7865
  `Farm plugin "${plugin.name}" runtime endpoint`
7718
7866
  );
7719
7867
  if (typeof endpoint.handler !== "function") {
7720
7868
  throw new TypeError(
7721
- `Farm plugin "${plugin.name}" runtime endpoint ${path17} requires a handler`
7869
+ `Farm plugin "${plugin.name}" runtime endpoint ${path18} requires a handler`
7722
7870
  );
7723
7871
  }
7724
- const owner = owners.get(path17);
7872
+ const owner = owners.get(path18);
7725
7873
  if (owner) {
7726
7874
  throw new Error(
7727
- `Farm plugin runtime endpoint ${path17} from "${plugin.name}" conflicts with "${owner}"`
7875
+ `Farm plugin runtime endpoint ${path18} from "${plugin.name}" conflicts with "${owner}"`
7728
7876
  );
7729
7877
  }
7730
- owners.set(path17, plugin.name);
7731
- endpoints.push({ plugin, endpoint, path: path17 });
7878
+ owners.set(path18, plugin.name);
7879
+ endpoints.push({ plugin, endpoint, path: path18 });
7732
7880
  }
7733
7881
  }
7734
7882
  this.runtimeEndpoints = endpoints;
@@ -8402,7 +8550,7 @@ __name(declarePluginSchema, "declarePluginSchema");
8402
8550
 
8403
8551
  // src/client-plugin-build.ts
8404
8552
  import fs6 from "fs";
8405
- import path11 from "path";
8553
+ import path12 from "path";
8406
8554
  var CLIENT_KEYS = [
8407
8555
  "public",
8408
8556
  "setup",
@@ -8416,9 +8564,9 @@ var HYDRATION_KEYS = ["before", "after"];
8416
8564
  var NAVIGATION_KEYS = ["before", "loaded", "resolved", "rendered", "error"];
8417
8565
  var APP_CLIENT_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mts", "mjs"];
8418
8566
  function resolveFarmAppClientEntry(root = process.cwd(), srcDir = "src") {
8419
- const sourceRoot = path11.resolve(root, srcDir);
8567
+ const sourceRoot = path12.resolve(root, srcDir);
8420
8568
  for (const extension of APP_CLIENT_EXTENSIONS) {
8421
- const candidate = path11.join(sourceRoot, `client.${extension}`);
8569
+ const candidate = path12.join(sourceRoot, `client.${extension}`);
8422
8570
  if (fs6.existsSync(candidate) && fs6.statSync(candidate).isFile()) return candidate;
8423
8571
  }
8424
8572
  return void 0;
@@ -8672,7 +8820,7 @@ __name(isArrayIndex, "isArrayIndex");
8672
8820
 
8673
8821
  // src/client-cache-persistence-build.ts
8674
8822
  import fs7 from "fs";
8675
- import path12 from "path";
8823
+ import path13 from "path";
8676
8824
  var ADAPTER_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mts", "mjs"];
8677
8825
  function resolveFarmClientCacheAdapterEntry(root, cache) {
8678
8826
  const client = cache?.client;
@@ -8683,7 +8831,7 @@ function resolveFarmClientCacheAdapterEntry(root, cache) {
8683
8831
  'cache.client.adapter must be a module path string, for example "./src/cache-adapter".'
8684
8832
  );
8685
8833
  }
8686
- const resolved = path12.resolve(root, adapter);
8834
+ const resolved = path13.resolve(root, adapter);
8687
8835
  const found = resolveAdapterFile(resolved);
8688
8836
  if (!found) {
8689
8837
  throw new Error(
@@ -8785,12 +8933,12 @@ function createFarmLayoutElement(Component, pattern, children, params) {
8785
8933
 
8786
8934
  // src/vite.ts
8787
8935
  import * as fs9 from "fs";
8788
- import * as path16 from "path";
8936
+ import * as path17 from "path";
8789
8937
  import { pathToFileURL as pathToFileURL3 } from "url";
8790
8938
 
8791
8939
  // src/dev-static.ts
8792
8940
  import * as fs8 from "fs";
8793
- import * as path13 from "path";
8941
+ import * as path14 from "path";
8794
8942
  function shouldBypassFarmRouterForDottedPath(pathname, routeManager, baseDirs, generatedPaths = []) {
8795
8943
  if (!pathname.includes(".") || pathname.endsWith(".html")) return false;
8796
8944
  const matchesAppRoute = Boolean(
@@ -8837,9 +8985,9 @@ function devServableFileExists(pathname, baseDirs) {
8837
8985
  if (!relativePathname) return false;
8838
8986
  for (const baseDir of baseDirs) {
8839
8987
  if (typeof baseDir !== "string" || baseDir.length === 0) continue;
8840
- const resolvedBase = path13.resolve(baseDir);
8841
- const candidate = path13.resolve(resolvedBase, relativePathname);
8842
- if (!candidate.startsWith(resolvedBase + path13.sep)) continue;
8988
+ const resolvedBase = path14.resolve(baseDir);
8989
+ const candidate = path14.resolve(resolvedBase, relativePathname);
8990
+ if (!candidate.startsWith(resolvedBase + path14.sep)) continue;
8843
8991
  try {
8844
8992
  if (fs8.existsSync(candidate) && fs8.statSync(candidate).isFile()) {
8845
8993
  return true;
@@ -8986,7 +9134,7 @@ __name(formatClientBoundaryWarning, "formatClientBoundaryWarning");
8986
9134
 
8987
9135
  // src/image-vite.ts
8988
9136
  import { readFile as readFile3 } from "fs/promises";
8989
- import path14 from "path";
9137
+ import path15 from "path";
8990
9138
  import { imageSize as imageSize2 } from "image-size";
8991
9139
  var FARM_IMAGE_ID_PREFIX = "\0farm:image:";
8992
9140
  var STATIC_IMAGE_RE = /\.(?:avif|gif|jpe?g|png|webp)$/i;
@@ -9054,7 +9202,7 @@ async function createBlurDataURL(bytes) {
9054
9202
  __name(createBlurDataURL, "createBlurDataURL");
9055
9203
 
9056
9204
  // src/server/vite-config.ts
9057
- import path15 from "path";
9205
+ import path16 from "path";
9058
9206
  var FARM_CLIENT_ENTRY_PATTERN = "**/{page,layout,loading,error,not-found,default}.{js,jsx,ts,tsx}";
9059
9207
  var FARM_CLIENT_OPTIMIZE_DEPS_INCLUDE = [
9060
9208
  "react",
@@ -9078,9 +9226,9 @@ function getFarmClientOptimizeDepsInclude(renderer) {
9078
9226
  }
9079
9227
  __name(getFarmClientOptimizeDepsInclude, "getFarmClientOptimizeDepsInclude");
9080
9228
  function createFarmClientOptimizeDepsEntries(projectRoot, appDirectories) {
9081
- const resolvedProjectRoot = path15.resolve(projectRoot);
9229
+ const resolvedProjectRoot = path16.resolve(projectRoot);
9082
9230
  return appDirectories.map((appDirectory) => {
9083
- const relativeAppDirectory = path15.relative(resolvedProjectRoot, path15.resolve(appDirectory)).replace(/\\/g, "/");
9231
+ const relativeAppDirectory = path16.relative(resolvedProjectRoot, path16.resolve(appDirectory)).replace(/\\/g, "/");
9084
9232
  const prefix = relativeAppDirectory || ".";
9085
9233
  return `${prefix}/${FARM_CLIENT_ENTRY_PATTERN}`;
9086
9234
  });
@@ -9104,7 +9252,7 @@ function createFarmClientOptimizeDepsConfig(entries, renderer) {
9104
9252
  __name(createFarmClientOptimizeDepsConfig, "createFarmClientOptimizeDepsConfig");
9105
9253
  function createFarmSourceAlias(projectRoot, srcDir = "src") {
9106
9254
  return {
9107
- "@": path15.resolve(projectRoot, srcDir)
9255
+ "@": path16.resolve(projectRoot, srcDir)
9108
9256
  };
9109
9257
  }
9110
9258
  __name(createFarmSourceAlias, "createFarmSourceAlias");
@@ -9300,6 +9448,7 @@ function reportOpenAPIDevGenerationResult(spec, output = logger) {
9300
9448
  __name(reportOpenAPIDevGenerationResult, "reportOpenAPIDevGenerationResult");
9301
9449
 
9302
9450
  // src/vite.ts
9451
+ var SERVER_FN_TRANSFORM_PLUGIN = "@farm.js/plugin/rsc:server-fn-actions";
9303
9452
  function farmLlmsTxtGeneratedPaths(config) {
9304
9453
  const llms = resolveFarmLlmsTxtConfig(config.agent?.llmsTxt);
9305
9454
  if (!llms.enabled) return [];
@@ -9353,7 +9502,7 @@ function loadFarmWorkflowsDevRuntime() {
9353
9502
  }
9354
9503
  __name(loadFarmWorkflowsDevRuntime, "loadFarmWorkflowsDevRuntime");
9355
9504
  function loadFarmDevtoolsSnapshotRuntime() {
9356
- return import("./devtools-MTB5SA2B.mjs");
9505
+ return import("./devtools-EZ5ORGAK.mjs");
9357
9506
  }
9358
9507
  __name(loadFarmDevtoolsSnapshotRuntime, "loadFarmDevtoolsSnapshotRuntime");
9359
9508
  function loadFarmDevtoolsUIRuntime() {
@@ -9761,7 +9910,7 @@ function fileContainsProgrammaticPageRoute(file) {
9761
9910
  __name(fileContainsProgrammaticPageRoute, "fileContainsProgrammaticPageRoute");
9762
9911
  function rewriteEarlySsrRelativeImports(options) {
9763
9912
  const cleanId = options.id.split("?", 1)[0];
9764
- if (!path16.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
9913
+ if (!path17.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
9765
9914
  return null;
9766
9915
  }
9767
9916
  const replacements = [];
@@ -9781,7 +9930,7 @@ function rewriteEarlySsrRelativeImports(options) {
9781
9930
  const suffixIndex = source.value.search(/[?#]/);
9782
9931
  const sourcePath = suffixIndex === -1 ? source.value : source.value.slice(0, suffixIndex);
9783
9932
  const suffix = suffixIndex === -1 ? "" : source.value.slice(suffixIndex);
9784
- const resolvedPath = path16.resolve(path16.dirname(cleanId), sourcePath);
9933
+ const resolvedPath = path17.resolve(path17.dirname(cleanId), sourcePath);
9785
9934
  replacements.push({
9786
9935
  start: source.start,
9787
9936
  end: source.end,
@@ -9941,6 +10090,7 @@ function farmPlugin(options = {}, initialPluginManager) {
9941
10090
  publicDir: options.publicDir
9942
10091
  });
9943
10092
  let farmApp;
10093
+ let serverFnTransformActive = false;
9944
10094
  let server;
9945
10095
  let apiRouteManager;
9946
10096
  let openAPIManager = null;
@@ -9979,7 +10129,7 @@ function farmPlugin(options = {}, initialPluginManager) {
9979
10129
  ...layerRoots.length ? {
9980
10130
  server: {
9981
10131
  fs: {
9982
- allow: [path16.resolve(options.root || process.cwd()), ...layerRoots]
10132
+ allow: [path17.resolve(options.root || process.cwd()), ...layerRoots]
9983
10133
  }
9984
10134
  }
9985
10135
  } : {}
@@ -9989,6 +10139,14 @@ function farmPlugin(options = {}, initialPluginManager) {
9989
10139
  if (typeof fontImports.configResolved === "function") {
9990
10140
  await fontImports.configResolved.call(this, config);
9991
10141
  }
10142
+ serverFnTransformActive = config.plugins.some(
10143
+ (plugin) => plugin.name === SERVER_FN_TRANSFORM_PLUGIN
10144
+ );
10145
+ if (options.experimental?.serverActions === true && !serverFnTransformActive) {
10146
+ logger.warn(
10147
+ "experimental.serverActions has no effect without @farm.js/plugin/rsc in the Vite plugins. Server functions stay server-only, and importing one from client code is a build error."
10148
+ );
10149
+ }
9992
10150
  },
9993
10151
  async configureServer(viteServer) {
9994
10152
  if (typeof fontImports.configureServer === "function") {
@@ -10040,7 +10198,7 @@ function farmPlugin(options = {}, initialPluginManager) {
10040
10198
  });
10041
10199
  }
10042
10200
  const sourceRoots = getFarmSourceRoots(farmConfig);
10043
- server.watcher.add(sourceRoots.map((source) => path16.join(source.root, source.srcDir)));
10201
+ server.watcher.add(sourceRoots.map((source) => path17.join(source.root, source.srcDir)));
10044
10202
  if (farmConfig.i18n.enabled) {
10045
10203
  server.watcher.add(
10046
10204
  farmConfig.i18n.locales.map(
@@ -10055,7 +10213,7 @@ function farmPlugin(options = {}, initialPluginManager) {
10055
10213
  ...farmDocsDevRuntime?.getFarmDocsRouteTypeEntries(farmConfig.docs) ?? []
10056
10214
  ], "getExtraRouteTypes");
10057
10215
  const appDirSlugs = sourceRoots.map(
10058
- (source) => path16.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
10216
+ (source) => path17.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
10059
10217
  );
10060
10218
  const generateTypeArtifacts = /* @__PURE__ */ __name(async (reason, selection = ALL_TYPE_ARTIFACTS, log = false) => {
10061
10219
  try {
@@ -10096,7 +10254,7 @@ function farmPlugin(options = {}, initialPluginManager) {
10096
10254
  }, "generateTypeArtifacts");
10097
10255
  await generateTypeArtifacts("startup");
10098
10256
  const srcDirSlugs = sourceRoots.map(
10099
- (source) => path16.join(source.root, source.srcDir).replace(/\\/g, "/")
10257
+ (source) => path17.join(source.root, source.srcDir).replace(/\\/g, "/")
10100
10258
  );
10101
10259
  const layerConfigFiles = new Set(
10102
10260
  farmConfig.layers.map((layer) => layer.configFile?.replace(/\\/g, "/")).filter((file) => Boolean(file))
@@ -10235,7 +10393,7 @@ function farmPlugin(options = {}, initialPluginManager) {
10235
10393
  await apiRouteManager.discoverRoutes();
10236
10394
  let discoveredWorkflows = [];
10237
10395
  const hasWorkflowDirectory = workflowConfig.enabled && workflowConfig.dirs.some(
10238
- (dir) => fs9.existsSync(path16.isAbsolute(dir) ? dir : path16.join(farmConfig.root, dir))
10396
+ (dir) => fs9.existsSync(path17.isAbsolute(dir) ? dir : path17.join(farmConfig.root, dir))
10239
10397
  );
10240
10398
  if (hasWorkflowDirectory) {
10241
10399
  const { createFarmWorkflowRequestHandler, discoverFarmWorkflows } = await loadFarmWorkflowsDevRuntime();
@@ -11688,7 +11846,7 @@ export const manifest = getManifest();
11688
11846
  async transform(code, id, transformOptions) {
11689
11847
  if (!transformOptions?.ssr) {
11690
11848
  const currentConfig = farmApp?.getConfig() ?? options;
11691
- if (currentConfig.experimental?.serverActions !== true) {
11849
+ if (currentConfig.experimental?.serverActions !== true || !serverFnTransformActive) {
11692
11850
  const violation = findClientServerFnViolation(code, id);
11693
11851
  if (violation) {
11694
11852
  this.error(formatServerFnBoundaryError(violation, id));
@@ -11742,7 +11900,7 @@ export const manifest = getManifest();
11742
11900
  const root = currentConfig.root || server?.config.root || process.cwd();
11743
11901
  const cleanId = id.split("?", 1)[0];
11744
11902
  const selectedIsolatedModules = options.isolatedClientBoundaryModules ?? farmApp?.getRouteManager().getIsolatedClientBoundaryModules(root);
11745
- const selectedForIsolatedHydration = selectedIsolatedModules?.has(path16.resolve(cleanId)) === true;
11903
+ const selectedForIsolatedHydration = selectedIsolatedModules?.has(path17.resolve(cleanId)) === true;
11746
11904
  if (isReactRenderer(resolveFarmRenderer(currentConfig.renderer)) && selectedForIsolatedHydration && isIsolatableClientBoundarySource(clientBoundarySource)) {
11747
11905
  isolatedModuleReference = toViteModuleId(cleanId, root);
11748
11906
  const transformedBoundary = transformIsolatedClientBoundaryModule({
@@ -11833,8 +11991,8 @@ if (import.meta.hot) {
11833
11991
  }
11834
11992
  logger.info(`Found ${ssgPages.length} SSG pages, ${ssrRoutes.length} SSR routes`);
11835
11993
  logger.info("Pre-rendering SSG pages...");
11836
- const outDir = path16.join(server?.config.root || process.cwd(), options.outDir || "dist");
11837
- const clientDir = path16.join(outDir, "client");
11994
+ const outDir = path17.join(server?.config.root || process.cwd(), options.outDir || "dist");
11995
+ const clientDir = path17.join(outDir, "client");
11838
11996
  for (const page of ssgPages) {
11839
11997
  try {
11840
11998
  const staticRequest = new Request(new URL(page.urlPath, "http://farm.static"));
@@ -11886,8 +12044,8 @@ if (import.meta.hot) {
11886
12044
  <script type="module" src="/assets/client.js"></script>
11887
12045
  </body>
11888
12046
  </html>`;
11889
- const outputPath = page.urlPath === "/" ? path16.join(clientDir, "index.html") : path16.join(clientDir, page.urlPath + ".html");
11890
- fs9.mkdirSync(path16.dirname(outputPath), { recursive: true });
12047
+ const outputPath = page.urlPath === "/" ? path17.join(clientDir, "index.html") : path17.join(clientDir, page.urlPath + ".html");
12048
+ fs9.mkdirSync(path17.dirname(outputPath), { recursive: true });
11891
12049
  fs9.writeFileSync(outputPath, fullHtml);
11892
12050
  const revalidateInfo = page.revalidate ? ` (revalidate: ${page.revalidate}s)` : "";
11893
12051
  logger.success(` \u2713 ${page.urlPath}${revalidateInfo}`);
@@ -11896,7 +12054,7 @@ if (import.meta.hot) {
11896
12054
  logger.error(` \u2717 ${page.urlPath}: ${error}`);
11897
12055
  }
11898
12056
  }
11899
- const manifestPath = path16.join(outDir, "__ssg_manifest.json");
12057
+ const manifestPath = path17.join(outDir, "__ssg_manifest.json");
11900
12058
  fs9.writeFileSync(
11901
12059
  manifestPath,
11902
12060
  JSON.stringify(
@@ -11940,9 +12098,9 @@ if (import.meta.hot) {
11940
12098
  server2.ws.send({ type: "full-reload", path: "*" });
11941
12099
  return [];
11942
12100
  }
11943
- const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path16.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
12101
+ const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path17.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
11944
12102
  if (currentSrcRoot && normalizedFile.startsWith(`${currentSrcRoot}/`) && isProgrammaticRoutesFileName(normalizedFile)) {
11945
- logUpdate("PAGE", `updated ${path16.basename(file)}`);
12103
+ logUpdate("PAGE", `updated ${path17.basename(file)}`);
11946
12104
  for (const mod of modules) {
11947
12105
  server2.moduleGraph.invalidateModule(mod);
11948
12106
  }
@@ -12073,10 +12231,10 @@ function createPageDataMiddlewareRequest(req, target) {
12073
12231
  __name(createPageDataMiddlewareRequest, "createPageDataMiddlewareRequest");
12074
12232
  function resolveDevelopmentRoot(farmRoot, viteRoot) {
12075
12233
  if (!farmRoot) return viteRoot;
12076
- if (path16.resolve(farmRoot) === path16.resolve(viteRoot)) return farmRoot;
12234
+ if (path17.resolve(farmRoot) === path17.resolve(viteRoot)) return farmRoot;
12077
12235
  try {
12078
12236
  if (fs9.realpathSync(farmRoot) === fs9.realpathSync(viteRoot)) {
12079
- return path16.resolve(viteRoot);
12237
+ return path17.resolve(viteRoot);
12080
12238
  }
12081
12239
  } catch {
12082
12240
  }
@@ -12436,8 +12594,11 @@ function generateClientCode(integrationProviders = [], documentNavigationMatcher
12436
12594
  srcDir,
12437
12595
  publicRuntimeConfig
12438
12596
  );
12597
+ const appSetupPath = resolveFarmRendererAppSetup(root, renderer);
12439
12598
  const rendererClientImports = isReactRenderer(renderer) ? `import React from 'react'
12440
- import { hydrateRoot, createRoot } from 'react-dom/client'` : `import React, { hydrateRoot, createRoot } from ${JSON.stringify(renderer.client)}`;
12599
+ import { hydrateRoot, createRoot } from 'react-dom/client'` : appSetupPath ? `import React, { hydrateRoot, createRoot, configureApp as __farmConfigureApp } from ${JSON.stringify(renderer.client)}
12600
+ import __farmAppSetup from ${JSON.stringify(toViteModuleId(appSetupPath, root))}
12601
+ __farmConfigureApp(__farmAppSetup)` : `import React, { hydrateRoot, createRoot } from ${JSON.stringify(renderer.client)}`;
12441
12602
  const isolatedHydrationImport = isolatedHydrationEnabled ? `import { createFarmIsolatedHydrationRuntime, createFarmServerPageBoundary, wrapFarmIsolatedClientGraph } from '@farm.js/core/internal/isolated-boundary'` : "";
12442
12603
  const docsAdapterImportBlock = generateFarmDocsAdapterClientRuntime(
12443
12604
  docsAdapterReact,
@@ -13994,8 +14155,8 @@ function generateClientManifest(bundle) {
13994
14155
  }
13995
14156
  __name(generateClientManifest, "generateClientManifest");
13996
14157
  async function defineConfig(config = {}) {
13997
- const tailwindcss = (await import("@tailwindcss/vite")).default;
13998
- const appRoot = path16.resolve(config.root || process.cwd());
14158
+ const tailwindVitePlugin = await loadTailwindVitePlugin();
14159
+ const appRoot = path17.resolve(config.root || process.cwd());
13999
14160
  if (config.extends?.length) {
14000
14161
  const { config: layeredConfig } = await import("./layers-CJKSJGLN.mjs").then(
14001
14162
  ({ resolveFarmLayers }) => resolveFarmLayers(config, {
@@ -14135,7 +14296,7 @@ async function defineConfig(config = {}) {
14135
14296
  return {
14136
14297
  plugins: [
14137
14298
  createFarmThemeCssPlugin(config.theme, config.basePath),
14138
- tailwindcss(),
14299
+ tailwindVitePlugin,
14139
14300
  ...rendererVitePlugins,
14140
14301
  viteBrowserExternalPlugin,
14141
14302
  farmI18nClientBridgePlugin(),
@@ -14231,6 +14392,7 @@ export {
14231
14392
  createFarmApp,
14232
14393
  FarmPluginOptionsError,
14233
14394
  farmPluginMayAffectRuntimePath,
14395
+ loadTailwindVitePlugin,
14234
14396
  generateFarmClientPluginEntryCode,
14235
14397
  resolveFarmClientCacheAdapterEntry,
14236
14398
  generateClientCachePersistenceCode,
@@ -3,6 +3,7 @@ import {
3
3
  } from "./chunk-LPEX4YW6.mjs";
4
4
 
5
5
  // src/renderer.ts
6
+ import fs from "fs";
6
7
  import { createRequire } from "module";
7
8
  import path from "path";
8
9
  import { pathToFileURL } from "url";
@@ -128,6 +129,9 @@ function resolveFarmRenderer(renderer) {
128
129
  throw new TypeError(`Farm renderer \`${field}\` must be a non-empty string.`);
129
130
  }
130
131
  }
132
+ if (resolved.appSetup !== void 0 && (typeof resolved.appSetup !== "string" || resolved.appSetup.trim().length === 0)) {
133
+ throw new TypeError("Farm renderer `appSetup` must be a non-empty module path.");
134
+ }
131
135
  return {
132
136
  ...resolved,
133
137
  componentExtensions: [...resolved.componentExtensions || []],
@@ -155,6 +159,15 @@ function isReactRenderer(renderer) {
155
159
  return !renderer || renderer.name === "react";
156
160
  }
157
161
  __name(isReactRenderer, "isReactRenderer");
162
+ function resolveFarmRendererAppSetup(root, renderer) {
163
+ if (!renderer.appSetup) return null;
164
+ const filePath = path.resolve(root, renderer.appSetup);
165
+ if (!fs.existsSync(filePath)) {
166
+ throw new Error(`Renderer \`${renderer.name}\` app setup module was not found: ${filePath}`);
167
+ }
168
+ return filePath;
169
+ }
170
+ __name(resolveFarmRendererAppSetup, "resolveFarmRendererAppSetup");
158
171
  function resolveFarmRendererModule(root, specifier) {
159
172
  const requireFromApp = createRequire(path.join(path.resolve(root), "package.json"));
160
173
  return requireFromApp.resolve(specifier);
@@ -201,6 +214,7 @@ export {
201
214
  resolveFarmRenderer,
202
215
  readFarmRendererWebStream,
203
216
  isReactRenderer,
217
+ resolveFarmRendererAppSetup,
204
218
  resolveFarmRendererModule,
205
219
  loadFarmRendererVitePlugins
206
220
  };