rari 0.2.21 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { HttpRuntimeClient, Link, Navigate, Outlet, RouteComponent, RouterProvider, Routes, buildSearchString, buildUrl, createHttpRuntimeClient, extractParamNames, findMatchingRoute, getRoutePriority, isDynamicRoute, isPathActive, joinPaths, normalizePathname, parseSearchParams, parseUrl, useNavigation, useParams, usePathname, useRoute, useRouter, useSearchParams, withRouter } from "./runtime-client-BXoadxa8.js";
2
- import { FileRouteGenerator, convertFilePatternToRoutePattern, createRouteManifest, defineRariConfig, defineRariOptions, generateFileRoutes, loadRouteManifest, rari, rariRouter, validateRoutes, watchFileRoutes } from "./server-D-ZsD18Z.js";
3
- import "./server-build-BM8_GFF5.js";
2
+ import { FileRouteGenerator, convertFilePatternToRoutePattern, createRouteManifest, defineRariConfig, defineRariOptions, generateFileRoutes, loadRouteManifest, rari, rariRouter, validateRoutes, watchFileRoutes } from "./server-C3POnIbO.js";
3
+ import "./server-build-DeJfuJb8.js";
4
4
 
5
5
  export { FileRouteGenerator, HttpRuntimeClient, Link, Navigate, Outlet, RouteComponent as Route, RouterProvider, Routes, buildSearchString, buildUrl, convertFilePatternToRoutePattern, createHttpRuntimeClient, createRouteManifest, defineRariConfig, defineRariOptions, extractParamNames, findMatchingRoute, generateFileRoutes, getRoutePriority, isDynamicRoute, isPathActive, joinPaths, loadRouteManifest, normalizePathname, parseSearchParams, parseUrl, rari, rariRouter, useNavigation, useParams, usePathname, useRoute, useRouter, useSearchParams, validateRoutes, watchFileRoutes, withRouter };
@@ -1,6 +1,6 @@
1
1
  import { __commonJS, __require, __toESM } from "./chunk-BLXvPPr8.js";
2
2
  import { analyzeFilePath } from "./runtime-client-BXoadxa8.js";
3
- import { createServerBuildPlugin } from "./server-build-BM8_GFF5.js";
3
+ import { createServerBuildPlugin } from "./server-build-DeJfuJb8.js";
4
4
  import fs, { promises } from "node:fs";
5
5
  import path from "node:path";
6
6
  import process$1 from "node:process";
@@ -5108,7 +5108,7 @@ var require_chokidar = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/ch
5108
5108
 
5109
5109
  //#endregion
5110
5110
  //#region src/router/file-routes.ts
5111
- var import_chokidar$1 = /* @__PURE__ */ __toESM(require_chokidar());
5111
+ var import_chokidar$1 = /* @__PURE__ */ __toESM(require_chokidar(), 1);
5112
5112
  var FileRouteGenerator = class {
5113
5113
  pagesDir;
5114
5114
  extensions;
@@ -5417,7 +5417,7 @@ function convertFilePatternToRoutePattern(pattern) {
5417
5417
 
5418
5418
  //#endregion
5419
5419
  //#region src/router/vite-plugin.ts
5420
- var import_chokidar = /* @__PURE__ */ __toESM(require_chokidar());
5420
+ var import_chokidar = /* @__PURE__ */ __toESM(require_chokidar(), 1);
5421
5421
  const DEFAULT_OPTIONS = {
5422
5422
  pagesDir: "src/pages",
5423
5423
  extensions: [
@@ -5636,11 +5636,16 @@ function scanForClientComponents(srcDir) {
5636
5636
  return clientComponents;
5637
5637
  }
5638
5638
  function extractCacheConfigFromContent(content) {
5639
- const ast = acorn.parse(content, {
5640
- ecmaVersion: 2022,
5641
- sourceType: "module",
5642
- allowImportExportEverywhere: true
5643
- });
5639
+ let ast;
5640
+ try {
5641
+ ast = acorn.parse(content, {
5642
+ ecmaVersion: 2022,
5643
+ sourceType: "module",
5644
+ allowImportExportEverywhere: true
5645
+ });
5646
+ } catch {
5647
+ return void 0;
5648
+ }
5644
5649
  for (const node of ast.body) if (node.type === "ExportNamedDeclaration" && node.declaration && node.declaration.type === "VariableDeclaration") {
5645
5650
  for (const declarator of node.declaration.declarations) if (declarator.id && declarator.id.name === "cacheConfig" && declarator.init && declarator.init.type === "ObjectExpression") {
5646
5651
  const config = {};
@@ -5688,7 +5693,7 @@ function rari(options = {}) {
5688
5693
  const serverDirectives = ["'use server'", "\"use server\""];
5689
5694
  const trimmedCode = code.trim();
5690
5695
  const hasServerDirective = serverDirectives.some((directive) => trimmedCode.startsWith(directive) || code.includes(directive));
5691
- const isInFunctionsDir = filePath.includes("/functions/") || filePath.includes("\\functions\\");
5696
+ const isInFunctionsDir = filePath.includes("/functions/") || filePath.includes("\\\\functions\\\\");
5692
5697
  const hasServerFunctionSignature = (code.includes("export async function") || code.includes("export function")) && code.includes("'use server'");
5693
5698
  if (hasServerDirective || isInFunctionsDir && hasServerFunctionSignature) return true;
5694
5699
  return false;
@@ -5975,6 +5980,11 @@ if (import.meta.hot) {
5975
5980
  import.meta.hot.accept();
5976
5981
  }
5977
5982
 
5983
+ if (typeof globalThis !== 'undefined') {
5984
+ globalThis.__rari_server_components = globalThis.__rari_server_components || new Set();
5985
+ globalThis.__rari_server_components.add(${JSON.stringify(id)});
5986
+ }
5987
+
5978
5988
  ${clientTransformedCode}`;
5979
5989
  return clientTransformedCode;
5980
5990
  }
@@ -6060,14 +6070,35 @@ const ${componentName$1} = registerClientReference(
6060
6070
  const srcDir = path.join(projectRoot, "src");
6061
6071
  const discoverAndRegisterComponents = async () => {
6062
6072
  try {
6063
- const { ServerComponentBuilder, scanDirectory } = await import("./server-build-DcbOtG3e.js");
6073
+ const { ServerComponentBuilder, scanDirectory } = await import("./server-build-Cs2pfA52.js");
6064
6074
  const builder = new ServerComponentBuilder(projectRoot, {
6065
6075
  outDir: "temp",
6066
6076
  serverDir: "server",
6067
6077
  manifestPath: "server-manifest.json"
6068
6078
  });
6069
6079
  const srcDir$1 = path.join(projectRoot, "src");
6070
- if (fs.existsSync(srcDir$1)) scanDirectory(srcDir$1, builder);
6080
+ const serverComponentPaths = [];
6081
+ if (fs.existsSync(srcDir$1)) {
6082
+ const collectServerComponents = (dir) => {
6083
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
6084
+ for (const entry of entries) {
6085
+ const fullPath = path.join(dir, entry.name);
6086
+ if (entry.isDirectory()) collectServerComponents(fullPath);
6087
+ else if (entry.isFile() && /\.(?:tsx?|jsx?)$/.test(entry.name)) try {
6088
+ if (isServerComponent(fullPath)) serverComponentPaths.push(fullPath);
6089
+ } catch (error) {
6090
+ console.warn(`[RARI] Error checking ${fullPath}:`, error);
6091
+ }
6092
+ }
6093
+ };
6094
+ collectServerComponents(srcDir$1);
6095
+ scanDirectory(srcDir$1, builder);
6096
+ }
6097
+ if (serverComponentPaths.length > 0) server.ws.send({
6098
+ type: "custom",
6099
+ event: "rari:server-components-registry",
6100
+ data: { serverComponents: serverComponentPaths }
6101
+ });
6071
6102
  const components = await builder.getTransformedComponentsForDevelopment();
6072
6103
  const serverPort = process$1.env.SERVER_PORT ? Number(process$1.env.SERVER_PORT) : Number(process$1.env.PORT || process$1.env.RSC_PORT || 3e3);
6073
6104
  const baseUrl = `http://localhost:${serverPort}`;
@@ -6195,7 +6226,7 @@ const ${componentName$1} = registerClientReference(
6195
6226
  };
6196
6227
  const handleServerComponentHMR = async (filePath) => {
6197
6228
  try {
6198
- const { ServerComponentBuilder } = await import("./server-build-DcbOtG3e.js");
6229
+ const { ServerComponentBuilder } = await import("./server-build-Cs2pfA52.js");
6199
6230
  const builder = new ServerComponentBuilder(projectRoot, {
6200
6231
  outDir: "temp",
6201
6232
  serverDir: "server",
@@ -6231,8 +6262,14 @@ const ${componentName$1} = registerClientReference(
6231
6262
  startRustServer();
6232
6263
  server.watcher.on("change", async (filePath) => {
6233
6264
  if (/\.(?:tsx?|jsx?)$/.test(filePath)) componentTypeCache.delete(filePath);
6234
- if (/\.(?:tsx?|jsx?)$/.test(filePath) && filePath.includes(srcDir)) if (isServerComponent(filePath)) await handleServerComponentHMR(filePath);
6235
- else setTimeout(discoverAndRegisterComponents, 1e3);
6265
+ if (/\.(?:tsx?|jsx?)$/.test(filePath) && filePath.includes(srcDir)) if (isServerComponent(filePath)) {
6266
+ server.ws.send({
6267
+ type: "custom",
6268
+ event: "rari:register-server-component",
6269
+ data: { filePath }
6270
+ });
6271
+ await handleServerComponentHMR(filePath);
6272
+ } else setTimeout(discoverAndRegisterComponents, 1e3);
6236
6273
  });
6237
6274
  server.middlewares.use("/api/vite/hmr-transform", async (req, res) => {
6238
6275
  if (req.method !== "POST") {
@@ -6586,7 +6623,9 @@ class RscClient {
6586
6623
  }
6587
6624
 
6588
6625
  async fetchServerComponent(componentId, props = {}) {
6589
- const cacheKey = componentId + ':' + JSON.stringify(props);
6626
+ const hmrCounter = (typeof window !== 'undefined' && window.__rscRefreshCounters && window.__rscRefreshCounters[componentId]) || 0;
6627
+ const cacheKey = componentId + ':' + JSON.stringify(props) + ':hmr:' + hmrCounter;
6628
+
6590
6629
 
6591
6630
  if (this.componentCache.has(cacheKey)) {
6592
6631
  return this.componentCache.get(cacheKey);
@@ -6832,14 +6871,15 @@ class RscClient {
6832
6871
  buffered = lines[lines.length - 1];
6833
6872
 
6834
6873
  for (const line of completeLines) {
6835
- if (!line.trim()) continue;
6874
+ if (!line.trim()) continue;
6875
+
6876
+ try {
6877
+ const colonIndex = line.indexOf(':');
6878
+ if (colonIndex === -1) continue;
6836
6879
 
6837
- try {
6838
- const colonIndex = line.indexOf(':');
6839
- if (colonIndex === -1) continue;
6880
+ const rowId = line.substring(0, colonIndex);
6881
+ const content = line.substring(colonIndex + 1);
6840
6882
 
6841
- const rowId = line.substring(0, colonIndex);
6842
- const content = line.substring(colonIndex + 1);
6843
6883
 
6844
6884
  if (content.includes('STREAM_COMPLETE')) {
6845
6885
  isComplete = true;
@@ -7374,6 +7414,9 @@ function createServerComponentWrapper(componentName, importPath) {
7374
7414
  const handleRscInvalidate = (event) => {
7375
7415
  const detail = event.detail;
7376
7416
  if (detail && detail.filePath && isServerComponent(detail.filePath)) {
7417
+
7418
+ rscClient.clearCache();
7419
+
7377
7420
  if (typeof window !== 'undefined') {
7378
7421
  window.__rscRefreshCounters[componentName] = (window.__rscRefreshCounters[componentName] || 0) + 1;
7379
7422
  setMountKey(window.__rscRefreshCounters[componentName]);
@@ -7409,17 +7452,48 @@ export const fetchServerComponent = (componentId, props) =>
7409
7452
 
7410
7453
  // Helper function to check if a file is a server component (client-side)
7411
7454
  function isServerComponent(filePath) {
7412
- // Simple client-side check based on file path patterns
7413
- return filePath && (
7414
- filePath.includes('ServerWithClient') ||
7415
- filePath.includes('server') ||
7416
- filePath.includes('Server')
7417
- );
7455
+ if (!filePath) {
7456
+ return false;
7457
+ }
7458
+
7459
+ try {
7460
+ if (typeof globalThis !== 'undefined' && globalThis.__rari_server_components) {
7461
+ return globalThis.__rari_server_components.has(filePath);
7462
+ }
7463
+
7464
+ const hasServerPattern = (
7465
+ filePath.includes('/functions/') ||
7466
+ filePath.includes('\\\\functions\\\\')
7467
+ );
7468
+
7469
+ return hasServerPattern;
7470
+ } catch (error) {
7471
+ console.warn('Error checking if file is server component:', error);
7472
+ return false;
7473
+ }
7418
7474
  }
7419
7475
 
7420
- // HMR support for RSC cache invalidation
7421
7476
  if (import.meta.hot) {
7422
- // Listen for Vite's beforeFullReload event for server components
7477
+ import.meta.hot.on('rari:register-server-component', (data) => {
7478
+ if (data?.filePath) {
7479
+ if (typeof globalThis !== 'undefined') {
7480
+ globalThis.__rari_server_components = globalThis.__rari_server_components || new Set();
7481
+ globalThis.__rari_server_components.add(data.filePath);
7482
+ }
7483
+ }
7484
+ });
7485
+
7486
+ import.meta.hot.on('rari:server-components-registry', (data) => {
7487
+ if (data?.serverComponents && Array.isArray(data.serverComponents)) {
7488
+ if (typeof globalThis !== 'undefined') {
7489
+ globalThis.__rari_server_components = globalThis.__rari_server_components || new Set();
7490
+ data.serverComponents.forEach(path => {
7491
+ globalThis.__rari_server_components.add(path);
7492
+ });
7493
+ }
7494
+ }
7495
+ });
7496
+
7423
7497
  import.meta.hot.on('vite:beforeFullReload', async (data) => {
7424
7498
  if (data?.path && isServerComponent(data.path)) {
7425
7499
  // Immediately invalidate cache and trigger re-registration before reload
@@ -7427,13 +7501,17 @@ if (import.meta.hot) {
7427
7501
  }
7428
7502
  });
7429
7503
 
7504
+ import.meta.hot.on('rari:server-component-updated', async (data) => {
7505
+ if (data?.path && isServerComponent(data.path)) {
7506
+ await invalidateRscCache({ filePath: data.path, forceReload: false });
7507
+ }
7508
+ });
7509
+
7430
7510
 
7431
7511
 
7432
- // Helper function to invalidate RSC cache and trigger component re-registration
7433
7512
  async function invalidateRscCache(data) {
7434
7513
  const filePath = data?.filePath || data;
7435
7514
 
7436
- // Wait for server to be ready
7437
7515
  const waitForServerReady = async () => {
7438
7516
  for (let i = 0; i < 20; i++) { // Try for up to 2 seconds
7439
7517
  try {
@@ -7530,9 +7608,11 @@ ${registrations.join("\n")}
7530
7608
  }
7531
7609
  },
7532
7610
  handleHotUpdate({ file, server }) {
7533
- if (/\.(?:tsx?|jsx?)$/.test(file) && isServerComponent(file)) {
7534
- server.hot.send("vite:beforeFullReload", {
7535
- type: "full-reload",
7611
+ const isReactFile = /\.(?:tsx?|jsx?)$/.test(file);
7612
+ const isServerComp = isServerComponent(file);
7613
+ if (isReactFile && isServerComp) {
7614
+ server.hot.send("rari:server-component-updated", {
7615
+ type: "rari-hmr",
7536
7616
  path: file
7537
7617
  });
7538
7618
  return [];
@@ -0,0 +1,3 @@
1
+ import { ServerComponentBuilder, createServerBuildPlugin, scanDirectory } from "./server-build-DeJfuJb8.js";
2
+
3
+ export { ServerComponentBuilder, scanDirectory };
@@ -32,7 +32,7 @@ var ServerComponentBuilder = class {
32
32
  ];
33
33
  const trimmedCode = code.trim();
34
34
  const hasServerDirective = serverDirectives.some((directive) => trimmedCode.startsWith(directive) || code.includes(directive));
35
- const isInFunctionsDir = filePath.includes("/functions/") || filePath.includes("\\functions\\");
35
+ const isInFunctionsDir = filePath.includes("/functions/") || filePath.includes("\\\\functions\\\\");
36
36
  const hasServerFunctionSignature = (code.includes("export async function") || code.includes("export function")) && code.includes("'use server'");
37
37
  const hasNodeImports = code.includes("from 'node:") || code.includes("from \"node:") || code.includes("from 'fs'") || code.includes("from \"fs\"") || code.includes("from 'path'") || code.includes("from \"path\"");
38
38
  const hasAsyncDefaultExport = /export\s+default\s+async\s+function/.test(code);
package/dist/server.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { HttpRuntimeClient, Link, Navigate, Outlet, RouteComponent, RouterProvider, Routes, buildSearchString, buildUrl, createHttpRuntimeClient, extractParamNames, findMatchingRoute, getRoutePriority, isDynamicRoute, isPathActive, joinPaths, normalizePathname, parseSearchParams, parseUrl, useNavigation, useParams, usePathname, useRoute, useRouter, useSearchParams, withRouter } from "./runtime-client-BXoadxa8.js";
2
- import { FileRouteGenerator, convertFilePatternToRoutePattern, createRouteManifest, defineRariConfig, defineRariOptions, generateFileRoutes, loadRouteManifest, rari, rariRouter, validateRoutes, watchFileRoutes } from "./server-D-ZsD18Z.js";
3
- import "./server-build-BM8_GFF5.js";
2
+ import { FileRouteGenerator, convertFilePatternToRoutePattern, createRouteManifest, defineRariConfig, defineRariOptions, generateFileRoutes, loadRouteManifest, rari, rariRouter, validateRoutes, watchFileRoutes } from "./server-C3POnIbO.js";
3
+ import "./server-build-DeJfuJb8.js";
4
4
 
5
5
  export { FileRouteGenerator, HttpRuntimeClient, Link, Navigate, Outlet, RouteComponent as Route, RouterProvider, Routes, buildSearchString, buildUrl, convertFilePatternToRoutePattern, createHttpRuntimeClient, createRouteManifest, defineRariConfig, defineRariOptions, extractParamNames, findMatchingRoute, generateFileRoutes, getRoutePriority, isDynamicRoute, isPathActive, joinPaths, loadRouteManifest, normalizePathname, parseSearchParams, parseUrl, rari, rariRouter, useNavigation, useParams, usePathname, useRoute, useRouter, useSearchParams, validateRoutes, watchFileRoutes, withRouter };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rari",
3
3
  "type": "module",
4
- "version": "0.2.21",
4
+ "version": "0.2.23",
5
5
  "description": "Runtime Accelerated Rendering Infrastructure (Rari)",
6
6
  "author": "Ryan Skinner",
7
7
  "license": "MIT",
@@ -77,11 +77,11 @@
77
77
  "picocolors": "^1.1.1"
78
78
  },
79
79
  "optionalDependencies": {
80
- "rari-darwin-arm64": "0.2.15",
81
- "rari-darwin-x64": "0.2.15",
82
- "rari-linux-arm64": "0.2.15",
83
- "rari-linux-x64": "0.2.15",
84
- "rari-win32-x64": "0.2.15"
80
+ "rari-darwin-arm64": "0.2.16",
81
+ "rari-darwin-x64": "0.2.16",
82
+ "rari-linux-arm64": "0.2.16",
83
+ "rari-linux-x64": "0.2.16",
84
+ "rari-win32-x64": "0.2.16"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@types/node": "^24.3.1",
package/src/vite/index.ts CHANGED
@@ -52,12 +52,20 @@ function scanForClientComponents(srcDir: string): Set<string> {
52
52
  return clientComponents
53
53
  }
54
54
 
55
- function extractCacheConfigFromContent(content: string): PageCacheConfig | undefined {
56
- const ast = acorn.parse(content, {
57
- ecmaVersion: 2022,
58
- sourceType: 'module',
59
- allowImportExportEverywhere: true,
60
- }) as any
55
+ function extractCacheConfigFromContent(
56
+ content: string,
57
+ ): PageCacheConfig | undefined {
58
+ let ast: any
59
+ try {
60
+ ast = acorn.parse(content, {
61
+ ecmaVersion: 2022,
62
+ sourceType: 'module',
63
+ allowImportExportEverywhere: true,
64
+ }) as any
65
+ }
66
+ catch {
67
+ return undefined
68
+ }
61
69
 
62
70
  for (const node of ast.body) {
63
71
  if (
@@ -81,9 +89,11 @@ function extractCacheConfigFromContent(content: string): PageCacheConfig | undef
81
89
  && prop.value
82
90
  && prop.value.type === 'Literal'
83
91
  ) {
84
- const keyName = prop.key.type === 'Literal' ? prop.key.value : prop.key.name
92
+ const keyName
93
+ = prop.key.type === 'Literal' ? prop.key.value : prop.key.name
85
94
  if (keyName === 'cache-control' || keyName === 'vary') {
86
- config[keyName as keyof PageCacheConfig] = prop.value.value as string
95
+ config[keyName as keyof PageCacheConfig] = prop.value
96
+ .value as string
87
97
  }
88
98
  }
89
99
  }
@@ -152,7 +162,7 @@ export function rari(options: RariOptions = {}): Plugin[] {
152
162
  )
153
163
 
154
164
  const isInFunctionsDir
155
- = filePath.includes('/functions/') || filePath.includes('\\functions\\')
165
+ = filePath.includes('/functions/') || filePath.includes('\\\\functions\\\\')
156
166
  const hasServerFunctionSignature
157
167
  = (code.includes('export async function')
158
168
  || code.includes('export function'))
@@ -645,6 +655,11 @@ if (import.meta.hot) {
645
655
  import.meta.hot.accept();
646
656
  }
647
657
 
658
+ if (typeof globalThis !== 'undefined') {
659
+ globalThis.__rari_server_components = globalThis.__rari_server_components || new Set();
660
+ globalThis.__rari_server_components.add(${JSON.stringify(id)});
661
+ }
662
+
648
663
  ${clientTransformedCode}`
649
664
  }
650
665
 
@@ -833,11 +848,41 @@ const ${componentName} = registerClientReference(
833
848
  })
834
849
 
835
850
  const srcDir = path.join(projectRoot, 'src')
851
+ const serverComponentPaths: string[] = []
836
852
 
837
853
  if (fs.existsSync(srcDir)) {
854
+ const collectServerComponents = (dir: string) => {
855
+ const entries = fs.readdirSync(dir, { withFileTypes: true })
856
+ for (const entry of entries) {
857
+ const fullPath = path.join(dir, entry.name)
858
+ if (entry.isDirectory()) {
859
+ collectServerComponents(fullPath)
860
+ }
861
+ else if (entry.isFile() && /\.(?:tsx?|jsx?)$/.test(entry.name)) {
862
+ try {
863
+ if (isServerComponent(fullPath)) {
864
+ serverComponentPaths.push(fullPath)
865
+ }
866
+ }
867
+ catch (error) {
868
+ console.warn(`[RARI] Error checking ${fullPath}:`, error)
869
+ }
870
+ }
871
+ }
872
+ }
873
+
874
+ collectServerComponents(srcDir)
838
875
  scanDirectory(srcDir, builder)
839
876
  }
840
877
 
878
+ if (serverComponentPaths.length > 0) {
879
+ server.ws.send({
880
+ type: 'custom',
881
+ event: 'rari:server-components-registry',
882
+ data: { serverComponents: serverComponentPaths },
883
+ })
884
+ }
885
+
841
886
  const components
842
887
  = await builder.getTransformedComponentsForDevelopment()
843
888
 
@@ -1118,6 +1163,11 @@ const ${componentName} = registerClientReference(
1118
1163
 
1119
1164
  if (/\.(?:tsx?|jsx?)$/.test(filePath) && filePath.includes(srcDir)) {
1120
1165
  if (isServerComponent(filePath)) {
1166
+ server.ws.send({
1167
+ type: 'custom',
1168
+ event: 'rari:register-server-component',
1169
+ data: { filePath },
1170
+ })
1121
1171
  await handleServerComponentHMR(filePath)
1122
1172
  }
1123
1173
  else {
@@ -1507,7 +1557,9 @@ class RscClient {
1507
1557
  }
1508
1558
 
1509
1559
  async fetchServerComponent(componentId, props = {}) {
1510
- const cacheKey = componentId + ':' + JSON.stringify(props);
1560
+ const hmrCounter = (typeof window !== 'undefined' && window.__rscRefreshCounters && window.__rscRefreshCounters[componentId]) || 0;
1561
+ const cacheKey = componentId + ':' + JSON.stringify(props) + ':hmr:' + hmrCounter;
1562
+
1511
1563
 
1512
1564
  if (this.componentCache.has(cacheKey)) {
1513
1565
  return this.componentCache.get(cacheKey);
@@ -1753,14 +1805,15 @@ class RscClient {
1753
1805
  buffered = lines[lines.length - 1];
1754
1806
 
1755
1807
  for (const line of completeLines) {
1756
- if (!line.trim()) continue;
1808
+ if (!line.trim()) continue;
1757
1809
 
1758
- try {
1759
- const colonIndex = line.indexOf(':');
1760
- if (colonIndex === -1) continue;
1810
+ try {
1811
+ const colonIndex = line.indexOf(':');
1812
+ if (colonIndex === -1) continue;
1813
+
1814
+ const rowId = line.substring(0, colonIndex);
1815
+ const content = line.substring(colonIndex + 1);
1761
1816
 
1762
- const rowId = line.substring(0, colonIndex);
1763
- const content = line.substring(colonIndex + 1);
1764
1817
 
1765
1818
  if (content.includes('STREAM_COMPLETE')) {
1766
1819
  isComplete = true;
@@ -2295,6 +2348,9 @@ function createServerComponentWrapper(componentName, importPath) {
2295
2348
  const handleRscInvalidate = (event) => {
2296
2349
  const detail = event.detail;
2297
2350
  if (detail && detail.filePath && isServerComponent(detail.filePath)) {
2351
+
2352
+ rscClient.clearCache();
2353
+
2298
2354
  if (typeof window !== 'undefined') {
2299
2355
  window.__rscRefreshCounters[componentName] = (window.__rscRefreshCounters[componentName] || 0) + 1;
2300
2356
  setMountKey(window.__rscRefreshCounters[componentName]);
@@ -2330,17 +2386,48 @@ export const fetchServerComponent = (componentId, props) =>
2330
2386
 
2331
2387
  // Helper function to check if a file is a server component (client-side)
2332
2388
  function isServerComponent(filePath) {
2333
- // Simple client-side check based on file path patterns
2334
- return filePath && (
2335
- filePath.includes('ServerWithClient') ||
2336
- filePath.includes('server') ||
2337
- filePath.includes('Server')
2338
- );
2389
+ if (!filePath) {
2390
+ return false;
2391
+ }
2392
+
2393
+ try {
2394
+ if (typeof globalThis !== 'undefined' && globalThis.__rari_server_components) {
2395
+ return globalThis.__rari_server_components.has(filePath);
2396
+ }
2397
+
2398
+ const hasServerPattern = (
2399
+ filePath.includes('/functions/') ||
2400
+ filePath.includes('\\\\functions\\\\')
2401
+ );
2402
+
2403
+ return hasServerPattern;
2404
+ } catch (error) {
2405
+ console.warn('Error checking if file is server component:', error);
2406
+ return false;
2407
+ }
2339
2408
  }
2340
2409
 
2341
- // HMR support for RSC cache invalidation
2342
2410
  if (import.meta.hot) {
2343
- // Listen for Vite's beforeFullReload event for server components
2411
+ import.meta.hot.on('rari:register-server-component', (data) => {
2412
+ if (data?.filePath) {
2413
+ if (typeof globalThis !== 'undefined') {
2414
+ globalThis.__rari_server_components = globalThis.__rari_server_components || new Set();
2415
+ globalThis.__rari_server_components.add(data.filePath);
2416
+ }
2417
+ }
2418
+ });
2419
+
2420
+ import.meta.hot.on('rari:server-components-registry', (data) => {
2421
+ if (data?.serverComponents && Array.isArray(data.serverComponents)) {
2422
+ if (typeof globalThis !== 'undefined') {
2423
+ globalThis.__rari_server_components = globalThis.__rari_server_components || new Set();
2424
+ data.serverComponents.forEach(path => {
2425
+ globalThis.__rari_server_components.add(path);
2426
+ });
2427
+ }
2428
+ }
2429
+ });
2430
+
2344
2431
  import.meta.hot.on('vite:beforeFullReload', async (data) => {
2345
2432
  if (data?.path && isServerComponent(data.path)) {
2346
2433
  // Immediately invalidate cache and trigger re-registration before reload
@@ -2348,13 +2435,17 @@ if (import.meta.hot) {
2348
2435
  }
2349
2436
  });
2350
2437
 
2438
+ import.meta.hot.on('rari:server-component-updated', async (data) => {
2439
+ if (data?.path && isServerComponent(data.path)) {
2440
+ await invalidateRscCache({ filePath: data.path, forceReload: false });
2441
+ }
2442
+ });
2443
+
2351
2444
 
2352
2445
 
2353
- // Helper function to invalidate RSC cache and trigger component re-registration
2354
2446
  async function invalidateRscCache(data) {
2355
2447
  const filePath = data?.filePath || data;
2356
2448
 
2357
- // Wait for server to be ready
2358
2449
  const waitForServerReady = async () => {
2359
2450
  for (let i = 0; i < 20; i++) { // Try for up to 2 seconds
2360
2451
  try {
@@ -2463,9 +2554,12 @@ ${registrations.join('\n')}
2463
2554
  },
2464
2555
 
2465
2556
  handleHotUpdate({ file, server }) {
2466
- if (/\.(?:tsx?|jsx?)$/.test(file) && isServerComponent(file)) {
2467
- server.hot.send('vite:beforeFullReload', {
2468
- type: 'full-reload',
2557
+ const isReactFile = /\.(?:tsx?|jsx?)$/.test(file)
2558
+ const isServerComp = isServerComponent(file)
2559
+
2560
+ if (isReactFile && isServerComp) {
2561
+ server.hot.send('rari:server-component-updated', {
2562
+ type: 'rari-hmr',
2469
2563
  path: file,
2470
2564
  })
2471
2565
  return []
@@ -2489,9 +2583,16 @@ ${registrations.join('\n')}
2489
2583
  const url = req.url || ''
2490
2584
  const pathname = url.split('?')[0]
2491
2585
 
2492
- if (pathname && !pathname.includes('.') && !pathname.startsWith('/api') && !pathname.startsWith('/rsc')) {
2586
+ if (
2587
+ pathname
2588
+ && !pathname.includes('.')
2589
+ && !pathname.startsWith('/api')
2590
+ && !pathname.startsWith('/rsc')
2591
+ ) {
2493
2592
  if (options.caching?.routes) {
2494
- for (const [pattern, cacheControl] of Object.entries(options.caching.routes)) {
2593
+ for (const [pattern, cacheControl] of Object.entries(
2594
+ options.caching.routes,
2595
+ )) {
2495
2596
  if (matchesPattern(pattern, pathname)) {
2496
2597
  res.setHeader('cache-control', cacheControl)
2497
2598
  break
@@ -2500,7 +2601,11 @@ ${registrations.join('\n')}
2500
2601
  }
2501
2602
 
2502
2603
  const pagePath = pathname === '/' ? '/index' : pathname
2503
- const pageFilePath = path.join(process.cwd(), 'src/pages', `${pagePath.slice(1) || 'index'}.tsx`)
2604
+ const pageFilePath = path.join(
2605
+ process.cwd(),
2606
+ 'src/pages',
2607
+ `${pagePath.slice(1) || 'index'}.tsx`,
2608
+ )
2504
2609
 
2505
2610
  if (fs.existsSync(pageFilePath)) {
2506
2611
  const pageContent = fs.readFileSync(pageFilePath, 'utf-8')
@@ -2521,8 +2626,15 @@ ${registrations.join('\n')}
2521
2626
  },
2522
2627
  writeBundle() {
2523
2628
  if (options.caching) {
2524
- const cacheConfigPath = path.join(process.cwd(), 'dist', 'cache-config.json')
2525
- fs.writeFileSync(cacheConfigPath, JSON.stringify(options.caching, null, 2))
2629
+ const cacheConfigPath = path.join(
2630
+ process.cwd(),
2631
+ 'dist',
2632
+ 'cache-config.json',
2633
+ )
2634
+ fs.writeFileSync(
2635
+ cacheConfigPath,
2636
+ JSON.stringify(options.caching, null, 2),
2637
+ )
2526
2638
  }
2527
2639
  },
2528
2640
  }
@@ -80,7 +80,7 @@ export class ServerComponentBuilder {
80
80
  )
81
81
 
82
82
  const isInFunctionsDir
83
- = filePath.includes('/functions/') || filePath.includes('\\functions\\')
83
+ = filePath.includes('/functions/') || filePath.includes('\\\\functions\\\\')
84
84
  const hasServerFunctionSignature
85
85
  = (code.includes('export async function')
86
86
  || code.includes('export function'))
@@ -1,3 +0,0 @@
1
- import { ServerComponentBuilder, createServerBuildPlugin, scanDirectory } from "./server-build-BM8_GFF5.js";
2
-
3
- export { ServerComponentBuilder, scanDirectory };