@farm.js/plugin 0.1.0-beta.10 → 0.1.0-beta.100

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 (70) hide show
  1. package/dist/api/index.d.ts +44 -0
  2. package/dist/api/index.d.ts.map +1 -0
  3. package/dist/api/index.js +497 -0
  4. package/dist/context/index.d.ts +61 -0
  5. package/dist/context/index.d.ts.map +1 -0
  6. package/dist/context/index.js +75 -0
  7. package/dist/dev-static.d.ts +8 -0
  8. package/dist/dev-static.d.ts.map +1 -0
  9. package/dist/dev-static.js +37 -0
  10. package/dist/index.d.ts +43 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +49 -0
  13. package/dist/middleware/index.d.ts +97 -0
  14. package/dist/middleware/index.d.ts.map +1 -0
  15. package/dist/middleware/index.js +444 -0
  16. package/dist/middleware/matching.d.ts +29 -0
  17. package/dist/middleware/matching.d.ts.map +1 -0
  18. package/dist/middleware/matching.js +79 -0
  19. package/dist/observability/index.d.ts +190 -0
  20. package/dist/observability/index.d.ts.map +1 -0
  21. package/dist/observability/index.js +399 -0
  22. package/dist/rsc/automatic-optimized-boundary.d.ts +11 -0
  23. package/dist/rsc/automatic-optimized-boundary.d.ts.map +1 -0
  24. package/dist/rsc/automatic-optimized-boundary.js +162 -0
  25. package/dist/rsc/build-paths.d.ts +3 -0
  26. package/dist/rsc/build-paths.d.ts.map +1 -0
  27. package/dist/rsc/build-paths.js +8 -0
  28. package/dist/rsc/compatibility.d.ts +8 -0
  29. package/dist/rsc/compatibility.d.ts.map +1 -0
  30. package/dist/rsc/compatibility.js +46 -0
  31. package/dist/rsc/dev-response.d.ts +3 -0
  32. package/dist/rsc/dev-response.d.ts.map +1 -0
  33. package/dist/rsc/dev-response.js +10 -0
  34. package/dist/rsc/entries/client.d.ts +15 -0
  35. package/dist/rsc/entries/client.d.ts.map +1 -0
  36. package/dist/rsc/entries/client.js +320 -0
  37. package/dist/rsc/entries/error-fallback.d.ts +3 -0
  38. package/dist/rsc/entries/error-fallback.d.ts.map +1 -0
  39. package/dist/rsc/entries/error-fallback.js +12 -0
  40. package/dist/rsc/entries/rsc.d.ts +13 -0
  41. package/dist/rsc/entries/rsc.d.ts.map +1 -0
  42. package/dist/rsc/entries/rsc.js +930 -0
  43. package/dist/rsc/entries/ssr.d.ts +13 -0
  44. package/dist/rsc/entries/ssr.d.ts.map +1 -0
  45. package/dist/rsc/entries/ssr.js +215 -0
  46. package/dist/rsc/index.d.ts +91 -0
  47. package/dist/rsc/index.d.ts.map +1 -0
  48. package/dist/rsc/index.js +1466 -0
  49. package/dist/rsc/nitro-build.d.ts +36 -0
  50. package/dist/rsc/nitro-build.d.ts.map +1 -0
  51. package/dist/rsc/nitro-build.js +416 -0
  52. package/dist/rsc/optimized-boundary.d.ts +29 -0
  53. package/dist/rsc/optimized-boundary.d.ts.map +1 -0
  54. package/dist/rsc/optimized-boundary.js +243 -0
  55. package/dist/rsc/route-action-transform.d.ts +11 -0
  56. package/dist/rsc/route-action-transform.d.ts.map +1 -0
  57. package/dist/rsc/route-action-transform.js +369 -0
  58. package/dist/rsc/server-fn-transform.d.ts +6 -0
  59. package/dist/rsc/server-fn-transform.d.ts.map +1 -0
  60. package/dist/rsc/server-fn-transform.js +152 -0
  61. package/dist/rsc/test-fixture-dependencies.d.ts +2 -0
  62. package/dist/rsc/test-fixture-dependencies.d.ts.map +1 -0
  63. package/dist/rsc/test-fixture-dependencies.js +23 -0
  64. package/dist/rsc/types.d.ts +132 -0
  65. package/dist/rsc/types.d.ts.map +1 -0
  66. package/dist/rsc/types.js +1 -0
  67. package/dist/rsc/vite-plugin-nitro.d.ts +46 -0
  68. package/dist/rsc/vite-plugin-nitro.d.ts.map +1 -0
  69. package/dist/rsc/vite-plugin-nitro.js +148 -0
  70. package/package.json +4 -4
@@ -0,0 +1,152 @@
1
+ const SERVER_FUNCTION_FACTORY_IMPORT_RE = /\bimport\s+(?:type\s+)?(?:[A-Za-z_$][\w$]*\s*,\s*)?(\{[^}]*\})\s+from\s*["']@farm.js\/core(?:\/(?:server-fn|server-query))?["']\s*;?/g;
2
+ const EXPORT_SERVER_FUNCTION_FACTORY_RE = /\bexport\s+(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([A-Za-z_$][\w$]*)\s*(?:<[^<>]*(?:<[^<>]*>[^<>]*)*>)?\s*\(/g;
3
+ const MODULE_EXT_RE = /\.[cm]?[jt]sx?$/;
4
+ const DECLARATION_RE = /(^|\n)\s*["']([^"']+)["']\s*;?/g;
5
+ export function transformFarmServerFns(code, id) {
6
+ if (!isTransformableModule(id) ||
7
+ (!code.includes("createServerFn") && !code.includes("createServerQuery"))) {
8
+ return null;
9
+ }
10
+ const factoryNames = findServerFunctionFactoryImportNames(code);
11
+ if (factoryNames.size === 0)
12
+ return null;
13
+ const replacements = findServerFnExportReplacements(code, factoryNames);
14
+ if (replacements.length === 0)
15
+ return null;
16
+ if (hasModuleDirective(code, "use client")) {
17
+ throw new Error("createServerFn and createServerQuery exports must live in a server module. Move them out of the 'use client' file.");
18
+ }
19
+ let output = code;
20
+ for (const replacement of [...replacements].sort((a, b) => b.start - a.start)) {
21
+ output = output.slice(0, replacement.start) + replacement.code + output.slice(replacement.end);
22
+ }
23
+ if (!hasModuleDirective(output, "use server")) {
24
+ output = `"use server";\n${output}`;
25
+ }
26
+ return {
27
+ code: output,
28
+ exports: replacements.map((replacement) => replacement.exportName),
29
+ };
30
+ }
31
+ function isTransformableModule(id) {
32
+ const cleanId = id.split("?", 1)[0];
33
+ return MODULE_EXT_RE.test(cleanId) && !/\.d\.[cm]?ts$/.test(cleanId);
34
+ }
35
+ function findServerFunctionFactoryImportNames(code) {
36
+ const names = new Map();
37
+ for (const match of code.matchAll(SERVER_FUNCTION_FACTORY_IMPORT_RE)) {
38
+ const importClause = match[1] ?? "";
39
+ const namedStart = importClause.indexOf("{");
40
+ const namedEnd = importClause.lastIndexOf("}");
41
+ if (namedStart === -1 || namedEnd === -1 || namedEnd <= namedStart)
42
+ continue;
43
+ const namedImports = importClause.slice(namedStart + 1, namedEnd).split(",");
44
+ for (const specifier of namedImports) {
45
+ const normalized = specifier.trim().replace(/^type\s+/, "");
46
+ const specifierMatch = normalized.match(/^(createServerFn|createServerQuery)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
47
+ if (specifierMatch) {
48
+ const factory = specifierMatch[1];
49
+ names.set(specifierMatch[2] ?? factory, factory === "createServerFn" ? "fn" : "query");
50
+ }
51
+ }
52
+ }
53
+ return names;
54
+ }
55
+ function findServerFnExportReplacements(code, factoryNames) {
56
+ const replacements = [];
57
+ for (const match of code.matchAll(EXPORT_SERVER_FUNCTION_FACTORY_RE)) {
58
+ const exportName = match[1];
59
+ const calleeName = match[2];
60
+ const factory = calleeName ? factoryNames.get(calleeName) : undefined;
61
+ if (!exportName || !calleeName || !factory)
62
+ continue;
63
+ const openParenIndex = (match.index ?? 0) + match[0].length - 1;
64
+ const callEnd = findMatchingParen(code, openParenIndex);
65
+ if (callEnd === -1)
66
+ continue;
67
+ let declarationEnd = callEnd + 1;
68
+ while (/\s/.test(code[declarationEnd] ?? ""))
69
+ declarationEnd++;
70
+ if (code[declarationEnd] === ";")
71
+ declarationEnd++;
72
+ const calleeIndex = code.indexOf(calleeName, match.index);
73
+ const callExpression = code.slice(calleeIndex, callEnd + 1);
74
+ const privateName = `$$farm_server_${factory}_${exportName}`;
75
+ replacements.push({
76
+ start: match.index ?? 0,
77
+ end: declarationEnd,
78
+ exportName,
79
+ code: `const ${privateName} = ${callExpression};\nexport async function ${exportName}(input) {\n return ${privateName}(input);\n}`,
80
+ });
81
+ }
82
+ return replacements;
83
+ }
84
+ function hasModuleDirective(code, directive) {
85
+ for (const match of code.matchAll(DECLARATION_RE)) {
86
+ const value = match[2];
87
+ if (value === directive)
88
+ return true;
89
+ if (value !== "use strict" && value !== "use client" && value !== "use server")
90
+ return false;
91
+ }
92
+ return false;
93
+ }
94
+ function findMatchingParen(code, openParenIndex) {
95
+ let depth = 0;
96
+ let quote = null;
97
+ let lineComment = false;
98
+ let blockComment = false;
99
+ let escaped = false;
100
+ for (let index = openParenIndex; index < code.length; index++) {
101
+ const char = code[index];
102
+ const next = code[index + 1];
103
+ if (lineComment) {
104
+ if (char === "\n" || char === "\r")
105
+ lineComment = false;
106
+ continue;
107
+ }
108
+ if (blockComment) {
109
+ if (char === "*" && next === "/") {
110
+ blockComment = false;
111
+ index++;
112
+ }
113
+ continue;
114
+ }
115
+ if (quote) {
116
+ if (escaped) {
117
+ escaped = false;
118
+ continue;
119
+ }
120
+ if (char === "\\") {
121
+ escaped = true;
122
+ continue;
123
+ }
124
+ if (char === quote) {
125
+ quote = null;
126
+ }
127
+ continue;
128
+ }
129
+ if (char === "/" && next === "/") {
130
+ lineComment = true;
131
+ index++;
132
+ continue;
133
+ }
134
+ if (char === "/" && next === "*") {
135
+ blockComment = true;
136
+ index++;
137
+ continue;
138
+ }
139
+ if (char === "'" || char === '"' || char === "`") {
140
+ quote = char;
141
+ continue;
142
+ }
143
+ if (char === "(")
144
+ depth++;
145
+ if (char === ")") {
146
+ depth--;
147
+ if (depth === 0)
148
+ return index;
149
+ }
150
+ }
151
+ return -1;
152
+ }
@@ -0,0 +1,2 @@
1
+ export declare function linkRscFixtureDependencies(root: string): void;
2
+ //# sourceMappingURL=test-fixture-dependencies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-fixture-dependencies.d.ts","sourceRoot":"","sources":["../../src/rsc/test-fixture-dependencies.ts"],"names":[],"mappings":"AAaA,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAY7D"}
@@ -0,0 +1,23 @@
1
+ import { existsSync, mkdirSync, realpathSync, symlinkSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const RSC_FIXTURE_PACKAGES = [
5
+ "react",
6
+ "react-dom",
7
+ "react-server-dom-webpack",
8
+ "@vitejs/plugin-rsc",
9
+ ];
10
+ const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../..");
11
+ export function linkRscFixtureDependencies(root) {
12
+ for (const packageName of RSC_FIXTURE_PACKAGES) {
13
+ const source = [
14
+ path.join(repositoryRoot, "node_modules", packageName),
15
+ path.join(repositoryRoot, "examples/rsc-demo/node_modules", packageName),
16
+ ].find((candidate) => existsSync(candidate));
17
+ if (!source)
18
+ throw new Error(`Missing RSC fixture dependency: ${packageName}`);
19
+ const target = path.join(root, "node_modules", packageName);
20
+ mkdirSync(path.dirname(target), { recursive: true });
21
+ symlinkSync(realpathSync(source), target, process.platform === "win32" ? "junction" : "dir");
22
+ }
23
+ }
@@ -0,0 +1,132 @@
1
+ import type { FarmServerActionsConfig, ResolvedFarmServerActionsConfig } from "@farm.js/core/server-action-security";
2
+ /**
3
+ * Configuration options for the Farm.js RSC plugin.
4
+ */
5
+ export interface FarmRscPluginOptions {
6
+ /** Security policy applied to server action requests. */
7
+ serverActions?: FarmServerActionsConfig;
8
+ /** Build identifier used to reject requests from stale browser deployments. */
9
+ deploymentId?: string;
10
+ /**
11
+ * Override the auto-generated virtual entries with custom files.
12
+ * If not provided, virtual entries are generated automatically based on
13
+ * the user's project structure.
14
+ */
15
+ entries?: {
16
+ /** Custom RSC environment entry file */
17
+ rsc?: string;
18
+ /** Custom SSR environment entry file */
19
+ ssr?: string;
20
+ /** Custom client environment entry file */
21
+ client?: string;
22
+ };
23
+ /**
24
+ * Enable encryption for server action bound arguments.
25
+ * This prevents exposing sensitive closure data in the client bundle.
26
+ * @default true
27
+ */
28
+ encryptActions?: boolean;
29
+ /**
30
+ * Subdirectory within srcDir containing route files.
31
+ * If empty, routes are discovered directly in srcDir.
32
+ * @default 'app'
33
+ */
34
+ routesDir?: string;
35
+ /**
36
+ * Custom CSS injection function.
37
+ * If not provided, uses import.meta.viteRsc.loadCss().
38
+ */
39
+ cssLoader?: string;
40
+ /**
41
+ * Enable verbose logging for debugging.
42
+ * @default false
43
+ */
44
+ debug?: boolean;
45
+ }
46
+ /**
47
+ * Internal context passed to entry generators.
48
+ * Contains resolved configuration values needed to generate entry files.
49
+ */
50
+ export interface EntryContext {
51
+ /** User's source directory, e.g. 'src' */
52
+ srcDir: string;
53
+ /** Build output directory, e.g. 'dist' */
54
+ outDir: string;
55
+ /** Base URL path, e.g. '/' */
56
+ basePath: string;
57
+ /** Resolved same-origin mount for canonical /api routes. */
58
+ apiBasePath?: string;
59
+ /** Route files subdirectory within srcDir */
60
+ routesDir?: string;
61
+ /** Root-relative Vite URL for an existing global stylesheet. */
62
+ globalCssPath?: string;
63
+ /** Ordered route roots. Modules from later roots override matching earlier modules. */
64
+ routeRoots?: Array<{
65
+ name: string;
66
+ base: string;
67
+ glob: string;
68
+ }>;
69
+ /** Whether server actions are enabled */
70
+ actionsEnabled: boolean;
71
+ /** Normalized server action request security policy. */
72
+ serverActions: ResolvedFarmServerActionsConfig;
73
+ /** Resolved build identifier embedded into the server and browser entries. */
74
+ deploymentId: string;
75
+ /** Whether debug mode is enabled */
76
+ debug: boolean;
77
+ /** True only for the Vite serve command, never for a production build. */
78
+ development?: boolean;
79
+ /** Generated import/init fragments for `cache.client.adapter` persistence. */
80
+ clientCachePersistence?: {
81
+ imports: string;
82
+ init: string;
83
+ };
84
+ }
85
+ /**
86
+ * Matched route information from the file-based router.
87
+ */
88
+ export interface MatchedRoute {
89
+ /** The page component's default export */
90
+ Page: React.ComponentType<any>;
91
+ /** The file pattern that matched, e.g. '/src/about/page.tsx' */
92
+ pattern: string;
93
+ /** Dynamic route parameters, e.g. { slug: 'my-post' } */
94
+ params: Record<string, string>;
95
+ }
96
+ /**
97
+ * RSC payload structure sent from server to client.
98
+ */
99
+ export interface RscPayload {
100
+ /** The root React element tree (full document for SSR) */
101
+ root: React.ReactElement;
102
+ /** Content for div#root only (for client hydration to avoid double/repeated content) */
103
+ rootContent?: React.ReactElement;
104
+ /** Serializable document metadata used during client-side navigation. */
105
+ metadata?: {
106
+ title?: string;
107
+ description?: string;
108
+ };
109
+ /** Server action return value (if action was executed) */
110
+ returnValue?: {
111
+ ok: boolean;
112
+ data: unknown;
113
+ };
114
+ /** Form state for progressive enhancement */
115
+ formState?: unknown;
116
+ }
117
+ /**
118
+ * Vite RSC module augmentation for import.meta.
119
+ */
120
+ declare global {
121
+ interface ImportMeta {
122
+ readonly viteRsc: {
123
+ /** Load CSS for server components */
124
+ loadCss(): React.ReactElement[];
125
+ /** Load a module from another environment */
126
+ loadModule<T = unknown>(env: string, entry: string): Promise<T>;
127
+ /** Load bootstrap script content for hydration */
128
+ loadBootstrapScriptContent(entry: string): Promise<string>;
129
+ };
130
+ }
131
+ }
132
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/rsc/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Vite plugin for the Nitro RSC deployment pipeline. It forces per-environment
3
+ * output dirs (`Step 1`), captures the server (RSC) Rollup bundle in `writeBundle`
4
+ * (`Step 2`), and exposes `runNitroFromBuildApp()`, which builds Nitro from the
5
+ * captured outputs once all environments have finished writing.
6
+ *
7
+ * Farm's RSC builder invokes `runNitroFromBuildApp()` after every environment
8
+ * has written its output, so a single `vite build` still produces the
9
+ * deployment bundle.
10
+ *
11
+ * The plugin no longer triggers Nitro itself from a Rollup hook: the previous
12
+ * `buildEnd` trigger fired before its own environment's `prepareOutDir`/`writeBundle`,
13
+ * so on a rebuild it observed the previous build's stale outputs and built Nitro
14
+ * against them (and set `__FARM_NITRO_PLUGIN_RAN`, suppressing the proper run).
15
+ * Removing it keeps Nitro from running against stale artifacts while the post-app
16
+ * hook preserves the one-command build contract.
17
+ *
18
+ * - Step 1: Force per-environment output dirs to `.nitro/vite/dist/${name}` so Nitro can locate artifacts.
19
+ * - Step 2: Capture the server (RSC) Rollup bundle in `writeBundle`.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { defineConfig, nitro } from '@farm.js/plugin/rsc'
24
+ * export default defineConfig({
25
+ * srcDir: 'src',
26
+ * plugins: [rsc(), react(), nitro({ server: { environmentName: 'rsc' }, config: { preset: 'vercel' } })],
27
+ * })
28
+ * ```
29
+ */
30
+ import type { Plugin } from "vite";
31
+ export interface NitroPluginOptions {
32
+ server?: {
33
+ environmentName?: string;
34
+ /** Entry name in rollupOptions.input (default: 'index'). */
35
+ entryName?: string;
36
+ };
37
+ config?: {
38
+ preset?: string;
39
+ };
40
+ /** Base output dir when not using .nitro/vite/dist (fallback). */
41
+ outDir?: string;
42
+ }
43
+ export default function nitro(options?: NitroPluginOptions): Plugin;
44
+ /** Called by RSC plugin buildApp (post) to run Nitro with the captured bundle. */
45
+ export declare function runNitroFromBuildApp(): Promise<void>;
46
+ //# sourceMappingURL=vite-plugin-nitro.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin-nitro.d.ts","sourceRoot":"","sources":["../../src/rsc/vite-plugin-nitro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAenC,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE;QACP,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,4DAA4D;QAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,OAAO,GAAE,kBAAuB,GAAG,MAAM,CAqEtE;AAED,kFAAkF;AAClF,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkC1D"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Vite plugin for the Nitro RSC deployment pipeline. It forces per-environment
3
+ * output dirs (`Step 1`), captures the server (RSC) Rollup bundle in `writeBundle`
4
+ * (`Step 2`), and exposes `runNitroFromBuildApp()`, which builds Nitro from the
5
+ * captured outputs once all environments have finished writing.
6
+ *
7
+ * Farm's RSC builder invokes `runNitroFromBuildApp()` after every environment
8
+ * has written its output, so a single `vite build` still produces the
9
+ * deployment bundle.
10
+ *
11
+ * The plugin no longer triggers Nitro itself from a Rollup hook: the previous
12
+ * `buildEnd` trigger fired before its own environment's `prepareOutDir`/`writeBundle`,
13
+ * so on a rebuild it observed the previous build's stale outputs and built Nitro
14
+ * against them (and set `__FARM_NITRO_PLUGIN_RAN`, suppressing the proper run).
15
+ * Removing it keeps Nitro from running against stale artifacts while the post-app
16
+ * hook preserves the one-command build contract.
17
+ *
18
+ * - Step 1: Force per-environment output dirs to `.nitro/vite/dist/${name}` so Nitro can locate artifacts.
19
+ * - Step 2: Capture the server (RSC) Rollup bundle in `writeBundle`.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { defineConfig, nitro } from '@farm.js/plugin/rsc'
24
+ * export default defineConfig({
25
+ * srcDir: 'src',
26
+ * plugins: [rsc(), react(), nitro({ server: { environmentName: 'rsc' }, config: { preset: 'vercel' } })],
27
+ * })
28
+ * ```
29
+ */
30
+ import path from "path";
31
+ import { buildRscNitro } from "./nitro-build.js";
32
+ import { waitForRscOutputs } from "./nitro-build.js";
33
+ import { resolveRscBuildOutputPath } from "./build-paths.js";
34
+ const NITRO_VITE_DIST = ".nitro/vite/dist";
35
+ export default function nitro(options = {}) {
36
+ const serverEnvName = options.server?.environmentName ?? "rsc";
37
+ const serverEntryName = options.server?.entryName ?? "index";
38
+ let resolvedRoot = process.cwd();
39
+ let rscOutDir = path.join(NITRO_VITE_DIST, "rsc");
40
+ let ssrOutDir = path.join(NITRO_VITE_DIST, "ssr");
41
+ let clientOutDir = path.join(NITRO_VITE_DIST, "client");
42
+ let clientAssetsDir = "assets";
43
+ return {
44
+ name: "vite-plugin-nitro",
45
+ apply: "build",
46
+ // Step 1: Force per-environment output dirs so Nitro can locate artifacts.
47
+ config() {
48
+ delete globalThis.__FARM_NITRO_SERVER_BUNDLE;
49
+ delete globalThis.__FARM_NITRO_PATHS;
50
+ delete globalThis.__FARM_NITRO_BUILD_PROMISE;
51
+ return {
52
+ environments: {
53
+ rsc: { build: { outDir: path.join(NITRO_VITE_DIST, "rsc") } },
54
+ ssr: { build: { outDir: path.join(NITRO_VITE_DIST, "ssr") } },
55
+ client: { build: { outDir: path.join(NITRO_VITE_DIST, "client") } },
56
+ },
57
+ };
58
+ },
59
+ configResolved(config) {
60
+ resolvedRoot = path.resolve(config.root ?? process.cwd());
61
+ const envs = config.environments;
62
+ if (envs?.rsc?.build?.outDir && envs?.ssr?.build?.outDir && envs?.client?.build?.outDir) {
63
+ rscOutDir = envs.rsc.build.outDir;
64
+ ssrOutDir = envs.ssr.build.outDir;
65
+ clientOutDir = envs.client.build.outDir;
66
+ }
67
+ else {
68
+ const baseOutDir = options.outDir ?? "dist";
69
+ rscOutDir = path.join(baseOutDir, "rsc");
70
+ ssrOutDir = path.join(baseOutDir, "ssr");
71
+ clientOutDir = path.join(baseOutDir, "client");
72
+ }
73
+ const clientBuildAssetsDir = envs?.client?.build?.assetsDir;
74
+ clientAssetsDir =
75
+ typeof clientBuildAssetsDir === "string" && clientBuildAssetsDir
76
+ ? clientBuildAssetsDir
77
+ : "assets";
78
+ },
79
+ // Step 2: Capture the server (RSC) Rollup bundle when that environment writes.
80
+ writeBundle(_options, bundle) {
81
+ if (this.environment?.name === serverEnvName) {
82
+ delete globalThis.__FARM_NITRO_BUILD_PROMISE;
83
+ globalThis.__FARM_NITRO_SERVER_BUNDLE = bundle;
84
+ globalThis.__FARM_NITRO_PATHS = {
85
+ root: resolvedRoot,
86
+ rscOutDir,
87
+ ssrOutDir,
88
+ clientOutDir,
89
+ serverEntryName,
90
+ preset: options.config?.preset ?? process.env.NITRO_PRESET ?? "vercel",
91
+ clientAssetsDir,
92
+ };
93
+ }
94
+ },
95
+ };
96
+ }
97
+ /** Called by RSC plugin buildApp (post) to run Nitro with the captured bundle. */
98
+ export async function runNitroFromBuildApp() {
99
+ const paths = globalThis.__FARM_NITRO_PATHS;
100
+ const bundle = globalThis.__FARM_NITRO_SERVER_BUNDLE;
101
+ if (!paths)
102
+ return;
103
+ const existing = globalThis.__FARM_NITRO_BUILD_PROMISE;
104
+ if (existing) {
105
+ await existing;
106
+ return;
107
+ }
108
+ const run = runCapturedNitroBuild(paths, bundle);
109
+ globalThis.__FARM_NITRO_BUILD_PROMISE = run;
110
+ try {
111
+ await run;
112
+ }
113
+ catch (error) {
114
+ if (globalThis.__FARM_NITRO_BUILD_PROMISE === run) {
115
+ delete globalThis.__FARM_NITRO_BUILD_PROMISE;
116
+ }
117
+ throw error;
118
+ }
119
+ }
120
+ async function runCapturedNitroBuild(paths, bundle) {
121
+ const root = paths.root;
122
+ const preset = paths.preset;
123
+ const baseOutDir = path.dirname(paths.rscOutDir);
124
+ await waitForRscOutputs(root, baseOutDir, { timeoutMs: 25_000 });
125
+ let rendererPath = resolveRscBuildOutputPath(root, paths.rscOutDir, "index.js");
126
+ if (bundle) {
127
+ const serverEntryChunks = [];
128
+ for (const chunk of Object.values(bundle)) {
129
+ if (chunk.type === "chunk" && chunk.isEntry && chunk.fileName) {
130
+ serverEntryChunks.push(chunk);
131
+ }
132
+ }
133
+ const selected = paths.serverEntryName
134
+ ? serverEntryChunks.find((c) => (c.name ?? c.fileName) === paths.serverEntryName)
135
+ : serverEntryChunks[0];
136
+ if (selected?.fileName) {
137
+ rendererPath = resolveRscBuildOutputPath(root, paths.rscOutDir, selected.fileName);
138
+ }
139
+ }
140
+ await buildRscNitro({
141
+ root,
142
+ rendererPath,
143
+ publicDir: resolveRscBuildOutputPath(root, paths.clientOutDir),
144
+ ssrPath: resolveRscBuildOutputPath(root, paths.ssrOutDir, "index.js"),
145
+ assetsDir: paths.clientAssetsDir ?? "assets",
146
+ preset,
147
+ });
148
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farm.js/plugin",
3
- "version": "0.1.0-beta.10",
3
+ "version": "0.1.0-beta.100",
4
4
  "description": "Official plugins for Farm.js framework",
5
5
  "license": "MIT",
6
6
  "author": "Farm.js Team",
@@ -61,7 +61,7 @@
61
61
  "nitro": "3.0.1-alpha.0",
62
62
  "picocolors": "^1.0.0",
63
63
  "rsc-html-stream": "^0.0.4",
64
- "@farm.js/core": "0.1.0-beta.10"
64
+ "@farm.js/core": "0.1.0-beta.100"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@types/node": "^20.19.21",
@@ -74,7 +74,7 @@
74
74
  "tsdown": "^0.15.7",
75
75
  "typescript": "^5.5.3",
76
76
  "vite": "^6.0.0",
77
- "vitest": "^1.1.0"
77
+ "vitest": "^3.2.7"
78
78
  },
79
79
  "peerDependencies": {
80
80
  "@vitejs/plugin-rsc": "0.5.32",
@@ -82,7 +82,7 @@
82
82
  "react-dom": "19.2.8",
83
83
  "react-server-dom-webpack": "19.2.8",
84
84
  "vite": ">=6.0.0",
85
- "@farm.js/core": "0.1.0-beta.10"
85
+ "@farm.js/core": "0.1.0-beta.100"
86
86
  },
87
87
  "peerDependenciesMeta": {
88
88
  "@vitejs/plugin-rsc": {