@farm.js/core 0.1.51-beta.1 → 0.1.51-beta.3

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 (187) hide show
  1. package/dist/{agent-mcp-handler-WWCC4GLG.mjs → agent-mcp-handler-7JSKGSOW.mjs} +4 -4
  2. package/dist/{agent-mcp-handler-RT3COCEL.cjs → agent-mcp-handler-YE4AGWHN.cjs} +5 -5
  3. package/dist/{agent-mcp.d-ZuuNgrkQ.d.ts → agent-mcp.d-BAR9njbJ.d.ts} +1 -1
  4. package/dist/{agent-mcp.d-BPzpKBHQ.d.mts → agent-mcp.d-BJKChOrO.d.mts} +1 -1
  5. package/dist/agent-runtime.d.mts +2 -2
  6. package/dist/agent-runtime.d.ts +2 -2
  7. package/dist/{analyzer-V74FTD2F.mjs → analyzer-OTPEWH5W.mjs} +82 -22
  8. package/dist/{analyzer-AZULAOPL.cjs → analyzer-TSQXZCZN.cjs} +82 -22
  9. package/dist/api/route-manager.d.mts +4 -4
  10. package/dist/api/route-manager.d.ts +4 -4
  11. package/dist/api/runtime.d.mts +3 -3
  12. package/dist/api/runtime.d.ts +3 -3
  13. package/dist/api.d.mts +4 -4
  14. package/dist/api.d.ts +4 -4
  15. package/dist/{app-manifest.d-HWhqbP2X.d.ts → app-manifest.d-BLFbaqx8.d.ts} +1 -1
  16. package/dist/{app-manifest.d-BJDcZn3W.d.mts → app-manifest.d-DEQNnNhV.d.mts} +1 -1
  17. package/dist/app-markdown.cjs +4 -4
  18. package/dist/app-markdown.d.mts +3 -3
  19. package/dist/app-markdown.d.ts +3 -3
  20. package/dist/app-markdown.mjs +3 -3
  21. package/dist/{app.d-B5EWdob-.d.ts → app.d-CNPaUYaa.d.ts} +4 -4
  22. package/dist/{app.d-DOGW1KI8.d.mts → app.d-CT7xDkj2.d.mts} +4 -4
  23. package/dist/{chunk-YVJV7Q2X.mjs → chunk-3BLEXUGE.mjs} +1 -1
  24. package/dist/chunk-3HLQTOVU.cjs +6 -0
  25. package/dist/{chunk-KU7MBLDZ.mjs → chunk-3SMXQNFD.mjs} +2 -2
  26. package/dist/{chunk-EWC76P5D.mjs → chunk-3TLKUBXG.mjs} +411 -166
  27. package/dist/{chunk-BSEKNTMS.mjs → chunk-3WSIYBMG.mjs} +41 -19
  28. package/dist/{chunk-JVBNKWLV.cjs → chunk-3XQ57EFI.cjs} +16 -16
  29. package/dist/{chunk-GW4YUIHW.mjs → chunk-3Z63X45F.mjs} +1 -1
  30. package/dist/{chunk-SCRC5AMM.mjs → chunk-4353ZRE5.mjs} +1 -1
  31. package/dist/{chunk-SBABQKPT.cjs → chunk-46KXZBOG.cjs} +5 -5
  32. package/dist/{chunk-2756SHCI.cjs → chunk-4CGOMZP3.cjs} +221 -199
  33. package/dist/{chunk-RMPNYUGT.cjs → chunk-4SEHMBL4.cjs} +10 -10
  34. package/dist/{chunk-QAIAFCRZ.mjs → chunk-4TKAIO2F.mjs} +3 -3
  35. package/dist/{chunk-NJLTQR5P.cjs → chunk-53SACYBY.cjs} +7 -7
  36. package/dist/{chunk-I3EFXUAR.mjs → chunk-5FXPJIOT.mjs} +2 -2
  37. package/dist/chunk-7FVVQEVL.cjs +541 -0
  38. package/dist/{chunk-IFBINMI4.cjs → chunk-7MD37P5V.cjs} +5 -5
  39. package/dist/{chunk-JATTVAHA.cjs → chunk-B3GM5I6N.cjs} +4 -4
  40. package/dist/chunk-C3BOESDW.mjs +541 -0
  41. package/dist/{chunk-WJFFPXCV.cjs → chunk-D7UQC4O6.cjs} +10 -8
  42. package/dist/{chunk-KRRZJ77U.mjs → chunk-E5DXF5GE.mjs} +2 -2
  43. package/dist/chunk-EFZGB7JC.cjs +114 -0
  44. package/dist/{chunk-NYQSC4DX.cjs → chunk-FFE2EMF7.cjs} +5 -5
  45. package/dist/{chunk-KMB5BMFX.mjs → chunk-GAO7MS5O.mjs} +9 -3
  46. package/dist/{chunk-HIFIG6HL.cjs → chunk-H33PJEXN.cjs} +5 -5
  47. package/dist/{chunk-TT66CP3P.cjs → chunk-HLWOWZFX.cjs} +2 -2
  48. package/dist/chunk-JXEPLIXD.cjs +389 -0
  49. package/dist/{chunk-VITZRYW3.cjs → chunk-KLNFJDFM.cjs} +15 -9
  50. package/dist/{chunk-KBRZAYI7.mjs → chunk-LDNSSHEG.mjs} +2 -2
  51. package/dist/chunk-LLZSYFOM.mjs +6 -0
  52. package/dist/{chunk-N77JBPLC.mjs → chunk-LPDKG6RA.mjs} +9 -7
  53. package/dist/chunk-MIGFX2FN.mjs +758 -0
  54. package/dist/{chunk-72D2NX77.mjs → chunk-MRNDAJEV.mjs} +1 -1
  55. package/dist/{chunk-5A5GAZ75.mjs → chunk-NOTSHVRN.mjs} +406 -234
  56. package/dist/{chunk-AGR7VJH2.cjs → chunk-OAMUSC7H.cjs} +1 -1
  57. package/dist/chunk-P6MHI5O2.cjs +758 -0
  58. package/dist/{chunk-EOSFVNXZ.mjs → chunk-R4PHCZ3G.mjs} +1 -1
  59. package/dist/{chunk-X2M4XN4V.cjs → chunk-TQMI6BYO.cjs} +675 -430
  60. package/dist/{chunk-76GHW3WI.cjs → chunk-TSFMFPRU.cjs} +4 -4
  61. package/dist/{chunk-5N7QAGEA.cjs → chunk-TWHXBMLJ.cjs} +37 -7
  62. package/dist/{chunk-BOVGCUDZ.cjs → chunk-UJL4JOXV.cjs} +2 -2
  63. package/dist/chunk-UQF3OJVE.mjs +389 -0
  64. package/dist/{chunk-XY4DYGSU.cjs → chunk-WGUHDWT5.cjs} +607 -435
  65. package/dist/{chunk-LAZK6UV7.mjs → chunk-YAJTD3ZG.mjs} +1 -1
  66. package/dist/{chunk-7UPCJDB5.mjs → chunk-YUEYBL35.mjs} +2 -2
  67. package/dist/{chunk-EIMK7D2D.mjs → chunk-ZD2VKAUI.mjs} +34 -4
  68. package/dist/chunk-ZNXTU4YH.mjs +114 -0
  69. package/dist/{client-plugins.d-C_3CdALC.d.mts → client-plugins.d-DllmH8eM.d.mts} +31 -5
  70. package/dist/{client-plugins.d-C0n8Yhh-.d.ts → client-plugins.d-n3iWY1EJ.d.ts} +31 -5
  71. package/dist/client-plugins.d.mts +2 -2
  72. package/dist/client-plugins.d.ts +2 -2
  73. package/dist/client.d.mts +7 -7
  74. package/dist/client.d.ts +7 -7
  75. package/dist/{config.d-CnhyvvmT.d.ts → config.d-DqKaCr1r.d.mts} +14 -3
  76. package/dist/{config.d-CnhyvvmT.d.mts → config.d-DqKaCr1r.d.ts} +14 -3
  77. package/dist/config.d.mts +2 -2
  78. package/dist/config.d.ts +2 -2
  79. package/dist/desktop.d.mts +3 -3
  80. package/dist/desktop.d.ts +3 -3
  81. package/dist/{devtools-KJFUE5FO.cjs → devtools-TO6TAO32.cjs} +5 -5
  82. package/dist/{devtools-BU5IT2PD.mjs → devtools-UESYARTF.mjs} +4 -4
  83. package/dist/docs.cjs +7 -7
  84. package/dist/docs.d.mts +3 -3
  85. package/dist/docs.d.ts +3 -3
  86. package/dist/docs.mjs +6 -6
  87. package/dist/index.cjs +23 -18
  88. package/dist/{index.d-CsGxS-hZ.d.mts → index.d-BL7KKiwa.d.mts} +1 -1
  89. package/dist/{index.d-B6M_8kmo.d.ts → index.d-BXsAXm1U.d.ts} +1 -1
  90. package/dist/index.d.mts +17 -17
  91. package/dist/index.d.ts +17 -17
  92. package/dist/index.mjs +23 -18
  93. package/dist/integrations.d.mts +2 -2
  94. package/dist/integrations.d.ts +2 -2
  95. package/dist/internal/app-markdown-runtime.cjs +3 -3
  96. package/dist/internal/app-markdown-runtime.d.mts +3 -3
  97. package/dist/internal/app-markdown-runtime.d.ts +3 -3
  98. package/dist/internal/app-markdown-runtime.mjs +2 -2
  99. package/dist/internal/build-runtime.cjs +21 -18
  100. package/dist/internal/build-runtime.d.mts +4 -4
  101. package/dist/internal/build-runtime.d.ts +4 -4
  102. package/dist/internal/build-runtime.mjs +20 -17
  103. package/dist/internal/cli-runtime.cjs +7 -7
  104. package/dist/internal/cli-runtime.mjs +6 -6
  105. package/dist/internal/client-error-overlay.cjs +4 -4
  106. package/dist/internal/client-error-overlay.mjs +1 -1
  107. package/dist/internal/client-runtime.d.mts +5 -5
  108. package/dist/internal/client-runtime.d.ts +5 -5
  109. package/dist/internal/compression-runtime.d.mts +2 -2
  110. package/dist/internal/compression-runtime.d.ts +2 -2
  111. package/dist/internal/config-runtime.cjs +6 -6
  112. package/dist/internal/config-runtime.d.mts +41 -80
  113. package/dist/internal/config-runtime.d.ts +41 -80
  114. package/dist/internal/config-runtime.mjs +5 -5
  115. package/dist/internal/infra-analytics-runtime.cjs +8 -8
  116. package/dist/internal/infra-analytics-runtime.mjs +6 -6
  117. package/dist/internal/product-telemetry-runtime.cjs +2 -2
  118. package/dist/internal/product-telemetry-runtime.mjs +1 -1
  119. package/dist/internal/production-runtime.cjs +91 -9
  120. package/dist/internal/production-runtime.d.mts +67 -12
  121. package/dist/internal/production-runtime.d.ts +67 -12
  122. package/dist/internal/production-runtime.mjs +90 -8
  123. package/dist/markdown.cjs +4 -2
  124. package/dist/markdown.d.mts +2 -2
  125. package/dist/markdown.d.ts +2 -2
  126. package/dist/markdown.mjs +3 -1
  127. package/dist/middleware.d.mts +4 -4
  128. package/dist/middleware.d.ts +4 -4
  129. package/dist/mobile.d.mts +21 -5
  130. package/dist/mobile.d.ts +21 -5
  131. package/dist/native-tree-EBPDFTRW.mjs +25 -0
  132. package/dist/native-tree-MMZS7S6W.cjs +25 -0
  133. package/dist/{navigation-delegate.d-C0BbdUgN.d.mts → navigation-delegate.d-C76gXTmO.d.mts} +1 -1
  134. package/dist/{navigation-delegate.d-jArRF0ZP.d.ts → navigation-delegate.d-DIyDslxt.d.ts} +1 -1
  135. package/dist/navigation.d.mts +5 -5
  136. package/dist/navigation.d.ts +5 -5
  137. package/dist/plugin.cjs +14 -13
  138. package/dist/plugin.d.mts +5 -4
  139. package/dist/plugin.d.ts +5 -4
  140. package/dist/plugin.mjs +13 -12
  141. package/dist/{production-runtime.d-DFaQ1qaV.d.mts → production-runtime.d-CDHztggO.d.mts} +1 -1
  142. package/dist/{production-runtime.d-UnXmSS_S.d.ts → production-runtime.d-tyaGdbk6.d.ts} +1 -1
  143. package/dist/{programmatic-routes-client.d-VZ0qcbH4.d.ts → programmatic-routes-client.d-XfmIrCK1.d.ts} +2 -2
  144. package/dist/{programmatic-routes-client.d-BIowUXQC.d.mts → programmatic-routes-client.d-aAWiDd64.d.mts} +2 -2
  145. package/dist/{render-plan.d-BNeqyPyJ.d.mts → render-plan.d-Diwybxo8.d.mts} +1 -1
  146. package/dist/{render-plan.d-BB6KrYjP.d.ts → render-plan.d-Dr5WRjoV.d.ts} +1 -1
  147. package/dist/renderer-client.d.mts +5 -5
  148. package/dist/renderer-client.d.ts +5 -5
  149. package/dist/{request.d-Bxhghm76.d.ts → request.d-D01fRn--.d.ts} +1 -1
  150. package/dist/{request.d-BV1munmx.d.mts → request.d-Dd3AFR79.d.mts} +1 -1
  151. package/dist/request.d.mts +3 -3
  152. package/dist/request.d.ts +3 -3
  153. package/dist/{route-rendering.d-DShqKlgK.d.mts → route-rendering.d-BQ53lJ_D.d.mts} +1 -1
  154. package/dist/{route-rendering.d-BIf7zE9d.d.ts → route-rendering.d-Bhx4QoJt.d.ts} +1 -1
  155. package/dist/{router.d-BE7i84fb.d.mts → router.d-Bi23a6Xc.d.mts} +1 -1
  156. package/dist/{router.d-Bw3K3b0a.d.ts → router.d-DQ3k7EYn.d.ts} +1 -1
  157. package/dist/{routes.d-_5tBQexL.d.mts → routes.d-BgTd7vI4.d.mts} +91 -4
  158. package/dist/{routes.d-DVZQL0-H.d.ts → routes.d-C7jMJFvP.d.ts} +91 -4
  159. package/dist/routes.d.mts +2 -2
  160. package/dist/routes.d.ts +2 -2
  161. package/dist/{rsc-pipeline-FBM6NFYY.mjs → rsc-pipeline-K3ER3Q4N.mjs} +5 -5
  162. package/dist/{rsc-pipeline-IQI5XQLS.cjs → rsc-pipeline-OCZIT5OF.cjs} +6 -6
  163. package/dist/{runtime-DXEVFYLE.cjs → runtime-DKNSIH7V.cjs} +85 -29
  164. package/dist/{runtime-2JYIDUON.mjs → runtime-MKYDEUZT.mjs} +75 -19
  165. package/dist/{runtime.d-CQgoYR0Q.d.mts → runtime.d-B2Js6qZ2.d.mts} +1 -1
  166. package/dist/{runtime.d-CfhaLPl-.d.ts → runtime.d-BL-CGwoi.d.ts} +1 -1
  167. package/dist/server-plugins.d.mts +3 -3
  168. package/dist/server-plugins.d.ts +3 -3
  169. package/dist/server.cjs +31 -30
  170. package/dist/server.d.mts +11 -10
  171. package/dist/server.d.ts +11 -10
  172. package/dist/server.mjs +16 -15
  173. package/dist/session-AAU7RNRO.cjs +23 -0
  174. package/dist/session-IGEDQ5PY.mjs +23 -0
  175. package/dist/{spa-router.d-D63jb6nX.d.mts → spa-router.d-B2rIFGs0.d.mts} +2 -2
  176. package/dist/{spa-router.d-C5opIgXg.d.ts → spa-router.d-DqZ6Lfs0.d.ts} +2 -2
  177. package/dist/testing.d.mts +2 -2
  178. package/dist/testing.d.ts +2 -2
  179. package/dist/{utils.d-CkpTqTsC.d.ts → utils.d-Bv2MPeLR.d.ts} +1 -1
  180. package/dist/{utils.d-CO-U4OVZ.d.mts → utils.d-fqXe8lNc.d.mts} +1 -1
  181. package/dist/version.cjs +2 -2
  182. package/dist/version.mjs +1 -1
  183. package/dist/vite.cjs +14 -13
  184. package/dist/vite.d.mts +12 -6
  185. package/dist/vite.d.ts +12 -6
  186. package/dist/vite.mjs +13 -12
  187. package/package.json +3 -3
@@ -1,10 +1,92 @@
1
1
  import { g as FarmI18nRouting } from './types.d-C41ePjvP.mjs';
2
- import { l as FarmMobileScreen, j as FarmMobilePresentation, i as FarmMobilePrefetch, h as FarmMobilePageConfig, F as FarmMobileOS, R as ResolvedFarmMobileConfig, m as FarmMobileTab } from './config.d-CnhyvvmT.mjs';
2
+ import { l as FarmMobileRouteScreen, m as FarmMobileScreen, j as FarmMobilePresentation, i as FarmMobilePrefetch, h as FarmMobilePageConfig, F as FarmMobileOS, R as ResolvedFarmMobileConfig, n as FarmMobileTab } from './config.d-DqKaCr1r.mjs';
3
3
  import { P as PlatformRouteFiles } from './generate-route-types.d-BoVDU8qX.mjs';
4
4
 
5
+ type FarmUiNodeKind = "View" | "Text" | "Image" | "Pressable" | "TextInput" | "List";
6
+ /** A minimal, platform-neutral UI tree for a portable component. No emitter reads it yet. */
7
+ interface FarmUiNode {
8
+ kind: FarmUiNodeKind | "Component" | "Fragment";
9
+ /** The JSX tag it came from, such as `"button"` or `"Card"`. */
10
+ tag: string;
11
+ /** Static props, such as `href`, `src`, `alt`, `placeholder`. Expressions are `"{expr}"`. */
12
+ props?: Record<string, string | number | boolean>;
13
+ /** Tailwind classes and inline style keys that map to native style. */
14
+ style?: string[];
15
+ /** Event props, such as `["onClick"]`. */
16
+ events?: string[];
17
+ /** Static text children. */
18
+ text?: string;
19
+ children?: FarmUiNode[];
20
+ }
21
+ interface FarmMobileBlocker {
22
+ /** App-root-relative POSIX path. */
23
+ file: string;
24
+ line: number;
25
+ column: number;
26
+ rule: FarmMobileBlockerRule;
27
+ message: string;
28
+ /** Internal: the one tier this blocks. Reported blockers are split by tier instead. */
29
+ only?: Exclude<FarmMobileTier, "web">;
30
+ }
31
+ /**
32
+ * What a route can become on mobile. `"native"`: the server renders the page into a tree of
33
+ * native views, with no JavaScript on the device (no hooks, event handlers, or client
34
+ * components). `"react-native"`: a React Native screen ported from the page. `"web"`: a WebView.
35
+ */
36
+ type FarmMobileTier = "native" | "react-native" | "web";
37
+ type FarmMobileBlockerRule = "parse-error" | "markdown-page" | "programmatic-route" | "server-component" | "async-component" | "unmapped-element" | "dangerously-set-inner-html" | "content-editable" | "dom-ref" | "dom-event" | "spread-props" | "form-action" | "dynamic-component" | "runtime-class-name" | "class-name" | "runtime-style" | "style" | "browser-global" | "layout-effect" | "create-element" | "css-import" | "dom-package" | "unknown-package" | "unmapped-import" | "unresolved-import" | "dynamic-import" | "node-builtin" | "client-logic";
38
+ type FarmMobileWin = "list" | "input" | "timer";
39
+ interface FarmMobileElementCounts {
40
+ /** JSX elements in the route's modules, intrinsic and imported components. */
41
+ total: number;
42
+ /** Elements with a native mapping. */
43
+ mapped: number;
44
+ byKind: Partial<Record<FarmUiNodeKind, number>>;
45
+ }
46
+ interface FarmMobileRouteAnalysis {
47
+ route: string;
48
+ /** The first tier no blocker rules out: `"native"`, then `"react-native"`, then `"web"`. */
49
+ tier: FarmMobileTier;
50
+ /**
51
+ * `"port"`: portable and native would help (lists, input, timers). `"web"`: blocked, or
52
+ * portable with no win. Any doubt is web.
53
+ */
54
+ decision: "port" | "web";
55
+ portable: boolean;
56
+ /** What keeps a React Native port out: the page's module graph first, then its layouts'. */
57
+ blockers: FarmMobileBlocker[];
58
+ /**
59
+ * What keeps the server-rendered native tier out. The page's graph only: a native screen shows
60
+ * the page without its layouts, since the shell draws the chrome.
61
+ */
62
+ nativeBlockers: FarmMobileBlocker[];
63
+ wins: FarmMobileWin[];
64
+ elements: FarmMobileElementCounts;
65
+ /** `mapped / total`, rounded, 100 for a route with no elements. */
66
+ portablePercent: number;
67
+ /** App modules in the route's graph, page and layouts. */
68
+ modules: number;
69
+ /** Byte size of `ir` as JSON. Portable routes only. */
70
+ irBytes?: number;
71
+ ir?: FarmMobileRouteIR;
72
+ }
73
+ interface FarmMobileRouteIR {
74
+ /** The page component, as `"<file>#default"`. */
75
+ root: string;
76
+ components: Record<string, FarmUiNode>;
77
+ }
78
+ interface FarmMobileAnalyzerStats {
79
+ /** Distinct app modules read. */
80
+ modules: number;
81
+ /** Modules parsed in this run; the rest came from the content-hash cache. */
82
+ parsed: number;
83
+ cached: number;
84
+ ms: number;
85
+ }
86
+
5
87
  type FarmMobileScreenSource = "page" | "config" | "file" | "analyzer" | "default";
6
88
  interface FarmMobileScreenDecision {
7
- screen: FarmMobileScreen;
89
+ screen: FarmMobileRouteScreen;
8
90
  source: FarmMobileScreenSource;
9
91
  /** App-root-relative file that decided (`"page"`), or the screen's file. */
10
92
  file?: string;
@@ -33,6 +115,11 @@ interface FarmMobileScreensOptions {
33
115
  analyzerPorts?: ReadonlySet<string>;
34
116
  /** Why the analyzer cannot port a route, for config routes that ask for a port. */
35
117
  analyzerBlockers?: ReadonlyMap<string, readonly string[]>;
118
+ /** Each analyzed route's tier, and what rules out the native tier. */
119
+ analyzerTiers?: ReadonlyMap<string, {
120
+ tier: FarmMobileTier;
121
+ nativeBlockers: readonly string[];
122
+ }>;
36
123
  }
37
124
  interface FarmMobileScreens {
38
125
  screens: Record<string, FarmMobileScreenDecision>;
@@ -72,7 +159,7 @@ interface FarmMobileRouteRule {
72
159
  /** `"tab"` switches to tab `tab` instead of presenting a screen. */
73
160
  presentation: FarmMobilePresentation | "tab";
74
161
  tab?: number;
75
- screen: FarmMobileScreen;
162
+ screen: FarmMobileRouteScreen;
76
163
  /** What decided `screen`: the page export, config, a screen file, the analyzer, or the default. */
77
164
  source?: FarmMobileScreenSource;
78
165
  /** Native module id derived from the route, such as `"app/notes/[id]"`. Not on web screens. */
@@ -118,4 +205,4 @@ declare function createFarmMobileRoutePattern(pattern: string, options?: Pick<Fa
118
205
  */
119
206
  declare function createFarmMobileRouteManifest(mobile: ResolvedFarmMobileConfig, options: FarmMobileRouteManifestOptions): FarmMobileRouteManifest;
120
207
 
121
- export { type FarmMobileRouteManifest as F, type FarmMobileRouteRule as a, type FarmMobileScreenSource as b, type FarmMobileScreenDecision as c, type FarmMobilePageExport as d, type FarmMobileRouteManifestOptions as e, type FarmMobileScreens as f, type FarmMobileScreensOptions as g, createFarmMobileModuleId as h, createFarmMobileRouteManifest as i, createFarmMobileRoutePattern as j, resolveFarmMobileScreens as k, readFarmMobilePageExport as r };
208
+ export { type FarmMobileRouteManifest as F, type FarmMobileRouteRule as a, type FarmMobileTier as b, type FarmMobileScreenSource as c, type FarmMobileRouteAnalysis as d, type FarmMobileScreenDecision as e, type FarmMobileAnalyzerStats as f, type FarmMobileBlocker as g, type FarmMobileBlockerRule as h, type FarmMobilePageExport as i, type FarmMobileRouteManifestOptions as j, type FarmMobileScreens as k, type FarmMobileScreensOptions as l, type FarmUiNode as m, createFarmMobileModuleId as n, createFarmMobileRouteManifest as o, createFarmMobileRoutePattern as p, resolveFarmMobileScreens as q, readFarmMobilePageExport as r };
@@ -1,10 +1,92 @@
1
1
  import { g as FarmI18nRouting } from './types.d-C41ePjvP.js';
2
- import { l as FarmMobileScreen, j as FarmMobilePresentation, i as FarmMobilePrefetch, h as FarmMobilePageConfig, F as FarmMobileOS, R as ResolvedFarmMobileConfig, m as FarmMobileTab } from './config.d-CnhyvvmT.js';
2
+ import { l as FarmMobileRouteScreen, m as FarmMobileScreen, j as FarmMobilePresentation, i as FarmMobilePrefetch, h as FarmMobilePageConfig, F as FarmMobileOS, R as ResolvedFarmMobileConfig, n as FarmMobileTab } from './config.d-DqKaCr1r.js';
3
3
  import { P as PlatformRouteFiles } from './generate-route-types.d-BL2pq6xC.js';
4
4
 
5
+ type FarmUiNodeKind = "View" | "Text" | "Image" | "Pressable" | "TextInput" | "List";
6
+ /** A minimal, platform-neutral UI tree for a portable component. No emitter reads it yet. */
7
+ interface FarmUiNode {
8
+ kind: FarmUiNodeKind | "Component" | "Fragment";
9
+ /** The JSX tag it came from, such as `"button"` or `"Card"`. */
10
+ tag: string;
11
+ /** Static props, such as `href`, `src`, `alt`, `placeholder`. Expressions are `"{expr}"`. */
12
+ props?: Record<string, string | number | boolean>;
13
+ /** Tailwind classes and inline style keys that map to native style. */
14
+ style?: string[];
15
+ /** Event props, such as `["onClick"]`. */
16
+ events?: string[];
17
+ /** Static text children. */
18
+ text?: string;
19
+ children?: FarmUiNode[];
20
+ }
21
+ interface FarmMobileBlocker {
22
+ /** App-root-relative POSIX path. */
23
+ file: string;
24
+ line: number;
25
+ column: number;
26
+ rule: FarmMobileBlockerRule;
27
+ message: string;
28
+ /** Internal: the one tier this blocks. Reported blockers are split by tier instead. */
29
+ only?: Exclude<FarmMobileTier, "web">;
30
+ }
31
+ /**
32
+ * What a route can become on mobile. `"native"`: the server renders the page into a tree of
33
+ * native views, with no JavaScript on the device (no hooks, event handlers, or client
34
+ * components). `"react-native"`: a React Native screen ported from the page. `"web"`: a WebView.
35
+ */
36
+ type FarmMobileTier = "native" | "react-native" | "web";
37
+ type FarmMobileBlockerRule = "parse-error" | "markdown-page" | "programmatic-route" | "server-component" | "async-component" | "unmapped-element" | "dangerously-set-inner-html" | "content-editable" | "dom-ref" | "dom-event" | "spread-props" | "form-action" | "dynamic-component" | "runtime-class-name" | "class-name" | "runtime-style" | "style" | "browser-global" | "layout-effect" | "create-element" | "css-import" | "dom-package" | "unknown-package" | "unmapped-import" | "unresolved-import" | "dynamic-import" | "node-builtin" | "client-logic";
38
+ type FarmMobileWin = "list" | "input" | "timer";
39
+ interface FarmMobileElementCounts {
40
+ /** JSX elements in the route's modules, intrinsic and imported components. */
41
+ total: number;
42
+ /** Elements with a native mapping. */
43
+ mapped: number;
44
+ byKind: Partial<Record<FarmUiNodeKind, number>>;
45
+ }
46
+ interface FarmMobileRouteAnalysis {
47
+ route: string;
48
+ /** The first tier no blocker rules out: `"native"`, then `"react-native"`, then `"web"`. */
49
+ tier: FarmMobileTier;
50
+ /**
51
+ * `"port"`: portable and native would help (lists, input, timers). `"web"`: blocked, or
52
+ * portable with no win. Any doubt is web.
53
+ */
54
+ decision: "port" | "web";
55
+ portable: boolean;
56
+ /** What keeps a React Native port out: the page's module graph first, then its layouts'. */
57
+ blockers: FarmMobileBlocker[];
58
+ /**
59
+ * What keeps the server-rendered native tier out. The page's graph only: a native screen shows
60
+ * the page without its layouts, since the shell draws the chrome.
61
+ */
62
+ nativeBlockers: FarmMobileBlocker[];
63
+ wins: FarmMobileWin[];
64
+ elements: FarmMobileElementCounts;
65
+ /** `mapped / total`, rounded, 100 for a route with no elements. */
66
+ portablePercent: number;
67
+ /** App modules in the route's graph, page and layouts. */
68
+ modules: number;
69
+ /** Byte size of `ir` as JSON. Portable routes only. */
70
+ irBytes?: number;
71
+ ir?: FarmMobileRouteIR;
72
+ }
73
+ interface FarmMobileRouteIR {
74
+ /** The page component, as `"<file>#default"`. */
75
+ root: string;
76
+ components: Record<string, FarmUiNode>;
77
+ }
78
+ interface FarmMobileAnalyzerStats {
79
+ /** Distinct app modules read. */
80
+ modules: number;
81
+ /** Modules parsed in this run; the rest came from the content-hash cache. */
82
+ parsed: number;
83
+ cached: number;
84
+ ms: number;
85
+ }
86
+
5
87
  type FarmMobileScreenSource = "page" | "config" | "file" | "analyzer" | "default";
6
88
  interface FarmMobileScreenDecision {
7
- screen: FarmMobileScreen;
89
+ screen: FarmMobileRouteScreen;
8
90
  source: FarmMobileScreenSource;
9
91
  /** App-root-relative file that decided (`"page"`), or the screen's file. */
10
92
  file?: string;
@@ -33,6 +115,11 @@ interface FarmMobileScreensOptions {
33
115
  analyzerPorts?: ReadonlySet<string>;
34
116
  /** Why the analyzer cannot port a route, for config routes that ask for a port. */
35
117
  analyzerBlockers?: ReadonlyMap<string, readonly string[]>;
118
+ /** Each analyzed route's tier, and what rules out the native tier. */
119
+ analyzerTiers?: ReadonlyMap<string, {
120
+ tier: FarmMobileTier;
121
+ nativeBlockers: readonly string[];
122
+ }>;
36
123
  }
37
124
  interface FarmMobileScreens {
38
125
  screens: Record<string, FarmMobileScreenDecision>;
@@ -72,7 +159,7 @@ interface FarmMobileRouteRule {
72
159
  /** `"tab"` switches to tab `tab` instead of presenting a screen. */
73
160
  presentation: FarmMobilePresentation | "tab";
74
161
  tab?: number;
75
- screen: FarmMobileScreen;
162
+ screen: FarmMobileRouteScreen;
76
163
  /** What decided `screen`: the page export, config, a screen file, the analyzer, or the default. */
77
164
  source?: FarmMobileScreenSource;
78
165
  /** Native module id derived from the route, such as `"app/notes/[id]"`. Not on web screens. */
@@ -118,4 +205,4 @@ declare function createFarmMobileRoutePattern(pattern: string, options?: Pick<Fa
118
205
  */
119
206
  declare function createFarmMobileRouteManifest(mobile: ResolvedFarmMobileConfig, options: FarmMobileRouteManifestOptions): FarmMobileRouteManifest;
120
207
 
121
- export { type FarmMobileRouteManifest as F, type FarmMobileRouteRule as a, type FarmMobileScreenSource as b, type FarmMobileScreenDecision as c, type FarmMobilePageExport as d, type FarmMobileRouteManifestOptions as e, type FarmMobileScreens as f, type FarmMobileScreensOptions as g, createFarmMobileModuleId as h, createFarmMobileRouteManifest as i, createFarmMobileRoutePattern as j, resolveFarmMobileScreens as k, readFarmMobilePageExport as r };
208
+ export { type FarmMobileRouteManifest as F, type FarmMobileRouteRule as a, type FarmMobileTier as b, type FarmMobileScreenSource as c, type FarmMobileRouteAnalysis as d, type FarmMobileScreenDecision as e, type FarmMobileAnalyzerStats as f, type FarmMobileBlocker as g, type FarmMobileBlockerRule as h, type FarmMobilePageExport as i, type FarmMobileRouteManifestOptions as j, type FarmMobileScreens as k, type FarmMobileScreensOptions as l, type FarmUiNode as m, createFarmMobileModuleId as n, createFarmMobileRouteManifest as o, createFarmMobileRoutePattern as p, resolveFarmMobileScreens as q, readFarmMobilePageExport as r };
package/dist/routes.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { v as FarmAppContext, w as PageProps, x as FarmSearchParams, y as PluginContextProps, f as FarmRouteRuntimeConfig, M as Metadata, i as RouteModule, L as LayoutProps, z as FarmRouteRuntime, A as FarmRouteRegions, C as FarmRouteMaxDuration, P as ParsedRoute } from './client-plugins.d-C_3CdALC.mjs';
1
+ import { v as FarmAppContext, w as PageProps, x as FarmSearchParams, y as PluginContextProps, f as FarmRouteRuntimeConfig, M as Metadata, i as RouteModule, L as LayoutProps, z as FarmRouteRuntime, A as FarmRouteRegions, C as FarmRouteMaxDuration, P as ParsedRoute } from './client-plugins.d-DllmH8eM.mjs';
2
2
  import { ComponentType } from 'react';
3
3
  import { RouteDataCacheKey } from './cache.mjs';
4
4
  import { ServerFn } from './server-fn.mjs';
@@ -27,7 +27,7 @@ import 'rolldown/utils';
27
27
  import 'rolldown';
28
28
  import './image-config.d-CeDgNyX4.mjs';
29
29
  import './types.d-C41ePjvP.mjs';
30
- import './config.d-CnhyvvmT.mjs';
30
+ import './config.d-DqKaCr1r.mjs';
31
31
  import './types.d-DQnyD3VD.mjs';
32
32
  import './font.mjs';
33
33
  import './deployment.mjs';
package/dist/routes.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { v as FarmAppContext, w as PageProps, x as FarmSearchParams, y as PluginContextProps, f as FarmRouteRuntimeConfig, M as Metadata, i as RouteModule, L as LayoutProps, z as FarmRouteRuntime, A as FarmRouteRegions, C as FarmRouteMaxDuration, P as ParsedRoute } from './client-plugins.d-C0n8Yhh-.js';
1
+ import { v as FarmAppContext, w as PageProps, x as FarmSearchParams, y as PluginContextProps, f as FarmRouteRuntimeConfig, M as Metadata, i as RouteModule, L as LayoutProps, z as FarmRouteRuntime, A as FarmRouteRegions, C as FarmRouteMaxDuration, P as ParsedRoute } from './client-plugins.d-n3iWY1EJ.js';
2
2
  import { ComponentType } from 'react';
3
3
  import { RouteDataCacheKey } from './cache.js';
4
4
  import { ServerFn } from './server-fn.js';
@@ -27,7 +27,7 @@ import 'rolldown/utils';
27
27
  import 'rolldown';
28
28
  import './image-config.d-CeDgNyX4.js';
29
29
  import './types.d-C41ePjvP.js';
30
- import './config.d-CnhyvvmT.js';
30
+ import './config.d-DqKaCr1r.js';
31
31
  import './types.d-DQnyD3VD.js';
32
32
  import './font.js';
33
33
  import './deployment.js';
@@ -2,7 +2,7 @@ import {
2
2
  buildFarmRsc,
3
3
  createFarmRscDevServer,
4
4
  createFarmRscViteConfig
5
- } from "./chunk-SCRC5AMM.mjs";
5
+ } from "./chunk-4353ZRE5.mjs";
6
6
  import {
7
7
  FARM_RSC_PACKAGES,
8
8
  farmAppUsesRscPipeline,
@@ -11,7 +11,7 @@ import {
11
11
  resolveFarmRscCheckConfig
12
12
  } from "./chunk-SHS6KXOZ.mjs";
13
13
  import "./chunk-LMFJCXDD.mjs";
14
- import "./chunk-KMB5BMFX.mjs";
14
+ import "./chunk-GAO7MS5O.mjs";
15
15
  import "./chunk-VISQDC6W.mjs";
16
16
  import "./chunk-QOSDWGKT.mjs";
17
17
  import "./chunk-PIHKO25G.mjs";
@@ -22,9 +22,9 @@ import "./chunk-X3XUZWRB.mjs";
22
22
  import "./chunk-XR2ZIBQK.mjs";
23
23
  import "./chunk-CU5SYTJG.mjs";
24
24
  import "./chunk-ES2LD2YD.mjs";
25
- import "./chunk-KBRZAYI7.mjs";
25
+ import "./chunk-LDNSSHEG.mjs";
26
26
  import "./chunk-2WZTGPK6.mjs";
27
- import "./chunk-LAZK6UV7.mjs";
27
+ import "./chunk-YAJTD3ZG.mjs";
28
28
  import "./chunk-22W4FNTG.mjs";
29
29
  import "./chunk-2NGZFOKG.mjs";
30
30
  import "./chunk-N33DLJBC.mjs";
@@ -36,7 +36,7 @@ import "./chunk-X3JNB2TB.mjs";
36
36
  import "./chunk-WXFY5JIY.mjs";
37
37
  import "./chunk-FSQMN2MQ.mjs";
38
38
  import "./chunk-YWPNIYUP.mjs";
39
- import "./chunk-N77JBPLC.mjs";
39
+ import "./chunk-LPDKG6RA.mjs";
40
40
  import "./chunk-YP7YO3S3.mjs";
41
41
  import "./chunk-Y2G5SJFB.mjs";
42
42
  import "./chunk-H4WUWLQR.mjs";
@@ -2,7 +2,7 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkTT66CP3Pcjs = require('./chunk-TT66CP3P.cjs');
5
+ var _chunkHLWOWZFXcjs = require('./chunk-HLWOWZFX.cjs');
6
6
 
7
7
 
8
8
 
@@ -11,7 +11,7 @@ var _chunkTT66CP3Pcjs = require('./chunk-TT66CP3P.cjs');
11
11
 
12
12
  var _chunkSJ23LJ3Ncjs = require('./chunk-SJ23LJ3N.cjs');
13
13
  require('./chunk-XPMC4E2G.cjs');
14
- require('./chunk-VITZRYW3.cjs');
14
+ require('./chunk-KLNFJDFM.cjs');
15
15
  require('./chunk-OZC2JPW6.cjs');
16
16
  require('./chunk-65Y5SIBH.cjs');
17
17
  require('./chunk-LNM57D3H.cjs');
@@ -22,9 +22,9 @@ require('./chunk-V56632LA.cjs');
22
22
  require('./chunk-LSHX2H5G.cjs');
23
23
  require('./chunk-STF6VDYQ.cjs');
24
24
  require('./chunk-RD623KW4.cjs');
25
- require('./chunk-NYQSC4DX.cjs');
25
+ require('./chunk-FFE2EMF7.cjs');
26
26
  require('./chunk-SG57PX7K.cjs');
27
- require('./chunk-HIFIG6HL.cjs');
27
+ require('./chunk-H33PJEXN.cjs');
28
28
  require('./chunk-6JHZPB52.cjs');
29
29
  require('./chunk-HEPJIKKQ.cjs');
30
30
  require('./chunk-3EIPOTZV.cjs');
@@ -36,7 +36,7 @@ require('./chunk-XYL4HSVC.cjs');
36
36
  require('./chunk-2KMIJDY6.cjs');
37
37
  require('./chunk-FK25QSVC.cjs');
38
38
  require('./chunk-WT6RAN2I.cjs');
39
- require('./chunk-WJFFPXCV.cjs');
39
+ require('./chunk-D7UQC4O6.cjs');
40
40
  require('./chunk-SHIWRBFI.cjs');
41
41
  require('./chunk-CECUDGXQ.cjs');
42
42
  require('./chunk-DUIP64BF.cjs');
@@ -80,4 +80,4 @@ require('./chunk-BTZ5O6VM.cjs');
80
80
 
81
81
 
82
82
 
83
- exports.FARM_RSC_PACKAGES = _chunkSJ23LJ3Ncjs.FARM_RSC_PACKAGES; exports.buildFarmRsc = _chunkTT66CP3Pcjs.buildFarmRsc; exports.createFarmRscDevServer = _chunkTT66CP3Pcjs.createFarmRscDevServer; exports.createFarmRscViteConfig = _chunkTT66CP3Pcjs.createFarmRscViteConfig; exports.farmAppUsesRscPipeline = _chunkSJ23LJ3Ncjs.farmAppUsesRscPipeline; exports.farmConfigUsesServerComponents = _chunkSJ23LJ3Ncjs.farmConfigUsesServerComponents; exports.inspectFarmServerComponents = _chunkSJ23LJ3Ncjs.inspectFarmServerComponents; exports.resolveFarmRscCheckConfig = _chunkSJ23LJ3Ncjs.resolveFarmRscCheckConfig;
83
+ exports.FARM_RSC_PACKAGES = _chunkSJ23LJ3Ncjs.FARM_RSC_PACKAGES; exports.buildFarmRsc = _chunkHLWOWZFXcjs.buildFarmRsc; exports.createFarmRscDevServer = _chunkHLWOWZFXcjs.createFarmRscDevServer; exports.createFarmRscViteConfig = _chunkHLWOWZFXcjs.createFarmRscViteConfig; exports.farmAppUsesRscPipeline = _chunkSJ23LJ3Ncjs.farmAppUsesRscPipeline; exports.farmConfigUsesServerComponents = _chunkSJ23LJ3Ncjs.farmConfigUsesServerComponents; exports.inspectFarmServerComponents = _chunkSJ23LJ3Ncjs.inspectFarmServerComponents; exports.resolveFarmRscCheckConfig = _chunkSJ23LJ3Ncjs.resolveFarmRscCheckConfig;
@@ -1,7 +1,10 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
+ var _chunkEFZGB7JCcjs = require('./chunk-EFZGB7JC.cjs');
3
4
 
4
- var _chunkVITZRYW3cjs = require('./chunk-VITZRYW3.cjs');
5
+
6
+
7
+ var _chunkKLNFJDFMcjs = require('./chunk-KLNFJDFM.cjs');
5
8
  require('./chunk-OZC2JPW6.cjs');
6
9
 
7
10
 
@@ -24,9 +27,9 @@ require('./chunk-STF6VDYQ.cjs');
24
27
 
25
28
 
26
29
  var _chunkRD623KW4cjs = require('./chunk-RD623KW4.cjs');
27
- require('./chunk-NYQSC4DX.cjs');
30
+ require('./chunk-FFE2EMF7.cjs');
28
31
  require('./chunk-SG57PX7K.cjs');
29
- require('./chunk-HIFIG6HL.cjs');
32
+ require('./chunk-H33PJEXN.cjs');
30
33
  require('./chunk-6JHZPB52.cjs');
31
34
  require('./chunk-HEPJIKKQ.cjs');
32
35
  require('./chunk-3EIPOTZV.cjs');
@@ -40,7 +43,7 @@ require('./chunk-XYL4HSVC.cjs');
40
43
  require('./chunk-2KMIJDY6.cjs');
41
44
  require('./chunk-FK25QSVC.cjs');
42
45
  require('./chunk-WT6RAN2I.cjs');
43
- require('./chunk-WJFFPXCV.cjs');
46
+ require('./chunk-D7UQC4O6.cjs');
44
47
  require('./chunk-SHIWRBFI.cjs');
45
48
  require('./chunk-CECUDGXQ.cjs');
46
49
  require('./chunk-DUIP64BF.cjs');
@@ -241,10 +244,8 @@ function validateFarmMobilePageConfig(value, where, context) {
241
244
  checkKeys(value, PAGE_KEYS, `${where}: mobile.`);
242
245
  checkPresentation(value.presentation, `${where}: mobile`, check);
243
246
  checkPrefetch(value.prefetch, `${where}: mobile.`, check);
244
- if (value.native !== void 0 && value.native !== false) {
245
- fail(
246
- `${where}: mobile.native can only be false. A native screen comes from a page.native.tsx next to the page.`
247
- );
247
+ if (value.native !== void 0 && typeof value.native !== "boolean") {
248
+ fail(`${where}: mobile.native must be true or false.`);
248
249
  }
249
250
  return value;
250
251
  }
@@ -455,7 +456,15 @@ function resolveFarmMobileBlock(input, context, cache) {
455
456
 
456
457
  // src/mobile/report.ts
457
458
  function createFarmMobileNativeReport(options) {
458
- const summary = { routes: 0, analyzed: 0, port: 0, portableNoWin: 0, blocked: 0, native: 0 };
459
+ const summary = {
460
+ routes: 0,
461
+ analyzed: 0,
462
+ port: 0,
463
+ portableNoWin: 0,
464
+ blocked: 0,
465
+ native: 0,
466
+ tiers: { native: 0, "react-native": 0, web: 0 }
467
+ };
459
468
  const routes = options.routes.map((route) => {
460
469
  const decision = options.screens[route];
461
470
  const analyzed = options.analysis[route];
@@ -463,6 +472,7 @@ function createFarmMobileNativeReport(options) {
463
472
  if (decision.screen !== "web") summary.native += 1;
464
473
  if (analyzed) {
465
474
  summary.analyzed += 1;
475
+ summary.tiers[analyzed.tier] += 1;
466
476
  if (analyzed.decision === "port") summary.port += 1;
467
477
  else if (analyzed.portable) summary.portableNoWin += 1;
468
478
  else summary.blocked += 1;
@@ -489,17 +499,20 @@ function createFarmMobileNativeReport(options) {
489
499
  function reasonFor(entry) {
490
500
  const analysis = entry.analysis;
491
501
  if (!analysis) {
492
- if (entry.source === "page") return `${entry.file}: mobile.native false`;
502
+ if (entry.source === "page") {
503
+ return `${entry.file}: mobile.native ${entry.requested ? "true" : "false"}`;
504
+ }
493
505
  if (entry.source === "file") return entry.file;
494
506
  if (entry.source === "config") {
495
507
  return entry.file ? `native.routes, ${entry.file}` : "native.routes";
496
508
  }
497
509
  return "";
498
510
  }
511
+ if (analysis.tier === "native") return "renders natively, no JavaScript on the device";
499
512
  if (analysis.portable) {
500
513
  return analysis.wins.length > 0 ? `portable; ${analysis.wins.join(", ")}` : `portable; no win (${analysis.elements.total} elements)`;
501
514
  }
502
- const [first, ...rest] = analysis.blockers;
515
+ const [first, ...rest] = analysis.nativeBlockers.length ? analysis.nativeBlockers : analysis.blockers;
503
516
  const where = first.file ? `${first.file}:${first.line}:${first.column} ` : "";
504
517
  return `${where}${first.message}${rest.length > 0 ? ` (+${rest.length} more)` : ""}`;
505
518
  }
@@ -507,12 +520,13 @@ function formatFarmMobileNativeReport(report, options = {}) {
507
520
  const rows = report.routes.map((entry) => [
508
521
  entry.route,
509
522
  entry.screen,
523
+ entry.analysis ? entry.analysis.tier : "-",
510
524
  entry.analysis ? entry.analysis.decision : "-",
511
525
  entry.analysis ? `${entry.analysis.elements.mapped}/${entry.analysis.elements.total}` : "",
512
526
  entry.analysis ? `${entry.analysis.portablePercent}%` : "",
513
527
  reasonFor(entry)
514
528
  ]);
515
- const header = ["route", "screen", "decision", "mapped", "portable", "reason"];
529
+ const header = ["route", "screen", "tier", "decision", "mapped", "portable", "reason"];
516
530
  const widths = header.map(
517
531
  (title, column) => Math.min(
518
532
  column === 0 ? 40 : 12,
@@ -529,7 +543,8 @@ function formatFarmMobileNativeReport(report, options = {}) {
529
543
  line(header),
530
544
  ...rows.map(line),
531
545
  "",
532
- `${summary.routes} routes: ${summary.port} port, ${summary.portableNoWin} portable without a win, ${summary.blocked} blocked, ${summary.native} native from files or config`
546
+ `${summary.routes} routes: ${summary.port} port, ${summary.portableNoWin} portable without a win, ${summary.blocked} blocked, ${summary.native} native from files or config`,
547
+ `tiers: ${summary.tiers.native} native, ${summary.tiers["react-native"]} react-native, ${summary.tiers.web} web`
533
548
  ];
534
549
  if (options.stats) {
535
550
  const { modules, parsed, cached, ms } = options.stats;
@@ -604,6 +619,32 @@ function resolveFarmMobileScreens(options) {
604
619
  screens[route] = { screen: "web", source: "page", file: page.file, ...base };
605
620
  continue;
606
621
  }
622
+ if (_optionalChain([page, 'optionalAccess', _30 => _30.config, 'access', _31 => _31.native]) === true) {
623
+ if (tier !== void 0 && tier !== "native") {
624
+ warnings.push(`${page.file}: mobile.native true overrides ${where}.${tierKey} "${tier}".`);
625
+ }
626
+ const file2 = _nullishCoalesce(reactFile, () => ( platformFile));
627
+ if (file2) {
628
+ screens[route] = {
629
+ screen: reactFile ? "native" : "platform",
630
+ source: "page",
631
+ file: relativeFile(root, file2),
632
+ module,
633
+ ...base
634
+ };
635
+ continue;
636
+ }
637
+ const analysis = _optionalChain([options, 'access', _32 => _32.analyzerTiers, 'optionalAccess', _33 => _33.get, 'call', _34 => _34(route)]);
638
+ if (analysis && analysis.tier !== "native") {
639
+ throw new TypeError(
640
+ `${prefix}: ${page.file} sets mobile.native to true, but "${route}" cannot render as a native screen:
641
+ ${analysis.nativeBlockers.map((blocker) => ` - ${blocker}`).join("\n")}
642
+ ` + (analysis.tier === "react-native" ? "It needs JavaScript on the device: add page.native.tsx next to the page, or " : "Remove the blockers, add page.native.tsx next to the page, or ") + "remove mobile.native."
643
+ );
644
+ }
645
+ screens[route] = analysis ? { screen: "server", source: "page", file: page.file, module, ...base } : { screen: "web", source: "page", file: page.file, requested: "native", ...base };
646
+ continue;
647
+ }
607
648
  if (tier === "web") {
608
649
  screens[route] = { screen: "web", source: "config", ...base };
609
650
  continue;
@@ -634,9 +675,13 @@ function resolveFarmMobileScreens(options) {
634
675
  };
635
676
  continue;
636
677
  }
637
- const blockers = _optionalChain([options, 'access', _30 => _30.analyzerBlockers, 'optionalAccess', _31 => _31.get, 'call', _32 => _32(route)]);
678
+ if (_optionalChain([options, 'access', _35 => _35.analyzerTiers, 'optionalAccess', _36 => _36.get, 'call', _37 => _37(route), 'optionalAccess', _38 => _38.tier]) === "native") {
679
+ screens[route] = { screen: "server", source: "config", module, ...base };
680
+ continue;
681
+ }
682
+ const blockers = _optionalChain([options, 'access', _39 => _39.analyzerBlockers, 'optionalAccess', _40 => _40.get, 'call', _41 => _41(route)]);
638
683
  warnings.push(
639
- `${where}.${tierKey} is "native", but "${route}" has no ${nativeTier === "native" ? "page.native.tsx" : platformFileName} and Farm cannot port pages yet, so it stays web` + (_optionalChain([blockers, 'optionalAccess', _33 => _33.length]) ? `. The analyzer would keep it web: ${blockers.join("; ")}` : "") + ". This fails the build once native screens ship."
684
+ `${where}.${tierKey} is "native", but "${route}" has no ${nativeTier === "native" ? "page.native.tsx" : platformFileName} and Farm cannot port pages yet, so it stays web` + (_optionalChain([blockers, 'optionalAccess', _42 => _42.length]) ? `. The analyzer would keep it web: ${blockers.join("; ")}` : "") + ". This fails the build once native screens ship."
640
685
  );
641
686
  screens[route] = { screen: "web", source: "config", requested: nativeTier, ...base };
642
687
  continue;
@@ -652,7 +697,11 @@ function resolveFarmMobileScreens(options) {
652
697
  };
653
698
  continue;
654
699
  }
655
- if (native.auto === "on" && _optionalChain([options, 'access', _34 => _34.analyzerPorts, 'optionalAccess', _35 => _35.has, 'call', _36 => _36(route)])) {
700
+ if (native.auto === "on" && _optionalChain([options, 'access', _43 => _43.analyzerTiers, 'optionalAccess', _44 => _44.get, 'call', _45 => _45(route), 'optionalAccess', _46 => _46.tier]) === "native") {
701
+ screens[route] = { screen: "server", source: "analyzer", module, ...base };
702
+ continue;
703
+ }
704
+ if (native.auto === "on" && _optionalChain([options, 'access', _47 => _47.analyzerPorts, 'optionalAccess', _48 => _48.has, 'call', _49 => _49(route)])) {
656
705
  screens[route] = { screen: "web", source: "analyzer", requested: nativeTier, ...base };
657
706
  continue;
658
707
  }
@@ -712,7 +761,7 @@ function readFarmMobilePageExport(source, file, parse, context) {
712
761
  const value = literalValue(declarator.init, fail);
713
762
  return validateFarmMobilePageConfig(value, file, context);
714
763
  }
715
- } else if (_optionalChain([declaration, 'access', _37 => _37.id, 'optionalAccess', _38 => _38.name]) === "mobile") {
764
+ } else if (_optionalChain([declaration, 'access', _50 => _50.id, 'optionalAccess', _51 => _51.name]) === "mobile") {
716
765
  fail('must be an object, such as export const mobile = { presentation: "modal" }');
717
766
  }
718
767
  }
@@ -818,10 +867,14 @@ function getFarmMobileOutputDirs(distDir) {
818
867
  ios: path2.join(root, "ios"),
819
868
  dist: path2.join(root, "dist"),
820
869
  report: path2.join(root, "native-report.json"),
821
- analyzerCache: path2.join(distDir, "cache", "mobile", "analyzer.json")
870
+ analyzerCache: path2.join(distDir, "cache", "mobile", "analyzer.json"),
871
+ client: path2.join(distDir, "client")
822
872
  };
823
873
  }
824
874
  var SCRIPT_PAGE = /\.(?:tsx|ts|jsx|js)$/;
875
+ function describe(blocker) {
876
+ return `${blocker.file ? `${blocker.file}:${blocker.line}:${blocker.column} ` : ""}${blocker.message}`;
877
+ }
825
878
  function scriptLang(file) {
826
879
  return file.endsWith(".tsx") ? "tsx" : file.endsWith(".ts") ? "ts" : "jsx";
827
880
  }
@@ -848,11 +901,11 @@ async function readPageExports(root, pageFiles, os) {
848
901
  return exports;
849
902
  }
850
903
  async function resolveFarmMobileApp(options = {}) {
851
- const root = path2.resolve(_nullishCoalesce(_nullishCoalesce(_optionalChain([options, 'access', _39 => _39.config, 'optionalAccess', _40 => _40.root]), () => ( options.root)), () => ( process.cwd())));
904
+ const root = path2.resolve(_nullishCoalesce(_nullishCoalesce(_optionalChain([options, 'access', _52 => _52.config, 'optionalAccess', _53 => _53.root]), () => ( options.root)), () => ( process.cwd())));
852
905
  const command = _nullishCoalesce(options.command, () => ( "build"));
853
906
  const mode = command === "dev" ? "development" : "production";
854
- const userConfig = options.userConfig === void 0 ? await _chunkVITZRYW3cjs.loadConfig.call(void 0, root, void 0, mode) : options.userConfig;
855
- if (_optionalChain([userConfig, 'optionalAccess', _41 => _41.mobile]) === void 0) {
907
+ const userConfig = options.userConfig === void 0 ? await _chunkKLNFJDFMcjs.loadConfig.call(void 0, root, void 0, mode) : options.userConfig;
908
+ if (_optionalChain([userConfig, 'optionalAccess', _54 => _54.mobile]) === void 0) {
856
909
  throw new FarmMobileError(
857
910
  'farm.config.ts has no `mobile` entry. Add one, such as `mobile: { id: "com.example.notes", server: "https://notes.example.com" }`.'
858
911
  );
@@ -865,7 +918,7 @@ async function resolveFarmMobileApp(options = {}) {
865
918
  mode
866
919
  });
867
920
  const block = await resolveFarmMobileBlock(userConfig.mobile, context, options.cache);
868
- const config = await _asyncNullishCoalesce(options.config, async () => ( await _chunkVITZRYW3cjs.resolveConfig.call(void 0, { ...userConfig, root }, mode)));
921
+ const config = await _asyncNullishCoalesce(options.config, async () => ( await _chunkKLNFJDFMcjs.resolveConfig.call(void 0, { ...userConfig, root }, mode)));
869
922
  const scan = await _chunkE36C43IXcjs.scanRoutePatterns.call(void 0, {
870
923
  root,
871
924
  srcDir: config.srcDir,
@@ -894,19 +947,20 @@ async function resolveFarmMobileApp(options = {}) {
894
947
  let { screens, warnings } = resolveFarmMobileScreens(screenOptions);
895
948
  let report;
896
949
  let analyzerStats;
897
- const analyze = options.analyze === "always" || options.analyze === true && mobile.native.auto !== "off";
950
+ const asserted = [...pageExports.values()].some((page) => page.config.native === true);
951
+ const analyze = options.analyze === "always" || asserted || options.analyze === true && mobile.native.auto !== "off";
898
952
  if (analyze) {
899
953
  const candidates = routes.filter((route) => {
900
954
  const decision = screens[route];
901
955
  return decision.source === "default" || decision.requested !== void 0;
902
956
  });
903
- const { analyzeFarmMobileRoutes } = await import("./analyzer-AZULAOPL.cjs");
957
+ const { analyzeFarmMobileRoutes } = await import("./analyzer-TSQXZCZN.cjs");
904
958
  const result = await analyzeFarmMobileRoutes({
905
959
  root,
906
960
  srcDir: config.srcDir,
907
961
  routes: candidates,
908
962
  pageFiles: scan.pageFiles,
909
- serverComponents: _chunkBPNOYILCcjs.resolveFarmServerComponentsMode.call(void 0, _optionalChain([config, 'access', _42 => _42.experimental, 'optionalAccess', _43 => _43.serverComponents])) !== void 0,
963
+ serverComponents: _chunkBPNOYILCcjs.resolveFarmServerComponentsMode.call(void 0, _optionalChain([config, 'access', _55 => _55.experimental, 'optionalAccess', _56 => _56.serverComponents])) !== void 0,
910
964
  cacheFile: path2.resolve(root, getFarmMobileOutputDirs(config.distDir).analyzerCache)
911
965
  });
912
966
  analyzerStats = result.stats;
@@ -916,11 +970,12 @@ async function resolveFarmMobileApp(options = {}) {
916
970
  Object.values(result.routes).filter((route) => route.decision === "port").map((route) => route.route)
917
971
  ),
918
972
  analyzerBlockers: new Map(
973
+ Object.values(result.routes).map((route) => [route.route, route.blockers.map(describe)])
974
+ ),
975
+ analyzerTiers: new Map(
919
976
  Object.values(result.routes).map((route) => [
920
977
  route.route,
921
- route.blockers.map(
922
- (blocker) => `${blocker.file ? `${blocker.file}:${blocker.line}:${blocker.column} ` : ""}${blocker.message}`
923
- )
978
+ { tier: route.tier, nativeBlockers: route.nativeBlockers.map(describe) }
924
979
  ])
925
980
  )
926
981
  }));
@@ -1002,4 +1057,5 @@ async function generateFarmMobileRoutes(options = {}) {
1002
1057
 
1003
1058
 
1004
1059
 
1005
- exports.FarmMobileError = FarmMobileError; exports.createFarmMobileContext = createFarmMobileContext; exports.createFarmMobileModuleId = createFarmMobileModuleId; exports.createFarmMobileRouteManifest = createFarmMobileRouteManifest; exports.createFarmMobileRoutePattern = createFarmMobileRoutePattern; exports.formatFarmMobileNativeReport = formatFarmMobileNativeReport; exports.generateFarmMobileRoutes = generateFarmMobileRoutes; exports.getFarmMobileOutputDirs = getFarmMobileOutputDirs; exports.readFarmMobilePageExport = readFarmMobilePageExport; exports.resolveFarmMobileApp = resolveFarmMobileApp; exports.resolveFarmMobileBlock = resolveFarmMobileBlock; exports.resolveFarmMobileConfig = resolveFarmMobileConfig; exports.resolveFarmMobileScreens = resolveFarmMobileScreens; exports.validateFarmMobileConfig = validateFarmMobileConfig;
1060
+
1061
+ exports.FarmMobileError = FarmMobileError; exports.createFarmMobileContext = createFarmMobileContext; exports.createFarmMobileModuleId = createFarmMobileModuleId; exports.createFarmMobileRouteManifest = createFarmMobileRouteManifest; exports.createFarmMobileRoutePattern = createFarmMobileRoutePattern; exports.formatFarmMobileNativeReport = formatFarmMobileNativeReport; exports.generateFarmMobileRoutes = generateFarmMobileRoutes; exports.getFarmMobileOutputDirs = getFarmMobileOutputDirs; exports.readFarmMobileClientManifest = _chunkEFZGB7JCcjs.readFarmMobileClientManifest; exports.readFarmMobilePageExport = readFarmMobilePageExport; exports.resolveFarmMobileApp = resolveFarmMobileApp; exports.resolveFarmMobileBlock = resolveFarmMobileBlock; exports.resolveFarmMobileConfig = resolveFarmMobileConfig; exports.resolveFarmMobileScreens = resolveFarmMobileScreens; exports.validateFarmMobileConfig = validateFarmMobileConfig;