Skip to content

Commit 0e49d15

Browse files
Refactor React on Rails package structure and enhance functionality
- Consolidated entry points in the knip configuration for both react-on-rails and react-on-rails-pro packages, improving project organization. - Introduced new base client and full objects to streamline the creation of ReactOnRails instances, enhancing code maintainability. - Added new methods for server-side rendering and component hydration in the Pro package, expanding functionality. - Updated package.json files to reflect new exports and improve module resolution. These changes enhance the overall structure and capabilities of the React on Rails framework.
1 parent a1e23ec commit 0e49d15

File tree

15 files changed

+556
-376
lines changed

15 files changed

+556
-376
lines changed

knip.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ const config: KnipConfig = {
3939

4040
// React on Rails core package workspace
4141
'packages/react-on-rails': {
42-
entry: [
43-
'src/ReactOnRails.node.ts!',
44-
],
42+
entry: ['src/ReactOnRails.node.ts!'],
4543
project: ['src/**/*.[jt]s{x,}!', 'tests/**/*.[jt]s{x,}', '!lib/**'],
4644
ignore: [
4745
// Jest setup and test utilities - not detected by Jest plugin in workspace setup
@@ -54,6 +52,9 @@ const config: KnipConfig = {
5452
// React on Rails Pro package workspace
5553
'packages/react-on-rails-pro': {
5654
entry: [
55+
'src/ReactOnRails.node.ts!',
56+
'src/ReactOnRails.full.ts!',
57+
'src/ReactOnRails.client.ts!',
5758
'src/index.ts!',
5859
'src/ReactOnRailsRSC.ts!',
5960
'src/registerServerComponent/client.tsx!',

packages/react-on-rails-pro/package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@
3030
"author": "[email protected]",
3131
"license": "UNLICENSED",
3232
"exports": {
33-
".": "./lib/index.js",
33+
".": {
34+
"node": "./lib/ReactOnRails.node.js",
35+
"default": "./lib/ReactOnRails.full.js"
36+
},
37+
"./client": "./lib/ReactOnRails.client.js",
38+
"./ReactOnRails.client": "./lib/ReactOnRails.client.js",
39+
"./ReactOnRails.full": "./lib/ReactOnRails.full.js",
40+
"./ReactOnRails.node": "./lib/ReactOnRails.node.js",
3441
"./registerServerComponent/client": "./lib/registerServerComponent/client.js",
3542
"./registerServerComponent/server": {
3643
"react-server": "./lib/registerServerComponent/server.rsc.js",
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) 2025 Shakacode LLC
3+
*
4+
* This file is NOT licensed under the MIT (open source) license.
5+
* It is part of the React on Rails Pro offering and is licensed separately.
6+
*
7+
* Unauthorized copying, modification, distribution, or use of this file,
8+
* via any medium, is strictly prohibited without a valid license agreement
9+
* from Shakacode LLC.
10+
*
11+
* For licensing terms, please see:
12+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
13+
*/
14+
15+
import { createBaseClientObject } from 'react-on-rails/@internal/base/client';
16+
import { createReactOnRailsPro } from './createReactOnRailsPro.ts';
17+
18+
const ReactOnRails = createReactOnRailsPro(createBaseClientObject);
19+
20+
export * from 'react-on-rails/types';
21+
export default ReactOnRails;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) 2025 Shakacode LLC
3+
*
4+
* This file is NOT licensed under the MIT (open source) license.
5+
* It is part of the React on Rails Pro offering and is licensed separately.
6+
*
7+
* Unauthorized copying, modification, distribution, or use of this file,
8+
* via any medium, is strictly prohibited without a valid license agreement
9+
* from Shakacode LLC.
10+
*
11+
* For licensing terms, please see:
12+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
13+
*/
14+
15+
import { createBaseFullObject } from 'react-on-rails/@internal/base/full';
16+
import { createReactOnRailsPro } from './createReactOnRailsPro.ts';
17+
18+
// Warn about bundle size when included in browser bundles
19+
if (typeof window !== 'undefined') {
20+
console.warn(
21+
'Optimization opportunity: "react-on-rails-pro" includes ~14KB of server-rendering code. ' +
22+
'Browsers may not need it. See https://forum.shakacode.com/t/how-to-use-different-versions-of-a-file-for-client-and-server-rendering/1352 ' +
23+
'(Requires creating a free account). Click this for the stack trace.',
24+
);
25+
}
26+
27+
const ReactOnRails = createReactOnRailsPro(createBaseFullObject);
28+
29+
export * from 'react-on-rails/types';
30+
export default ReactOnRails;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) 2025 Shakacode LLC
3+
*
4+
* This file is NOT licensed under the MIT (open source) license.
5+
* It is part of the React on Rails Pro offering and is licensed separately.
6+
*
7+
* Unauthorized copying, modification, distribution, or use of this file,
8+
* via any medium, is strictly prohibited without a valid license agreement
9+
* from Shakacode LLC.
10+
*
11+
* For licensing terms, please see:
12+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
13+
*/
14+
15+
import ReactOnRails from './ReactOnRails.full.ts';
16+
import streamServerRenderedReactComponent from './streamServerRenderedReactComponent.ts';
17+
18+
// Add Pro server-side streaming functionality
19+
ReactOnRails.streamServerRenderedReactComponent = streamServerRenderedReactComponent;
20+
21+
export * from './ReactOnRails.full.ts';
22+
// eslint-disable-next-line no-restricted-exports -- see https://github.com/eslint/eslint/issues/15617
23+
export { default } from './ReactOnRails.full.ts';

packages/react-on-rails-pro/src/ReactOnRailsRSC.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import {
2222
StreamRenderState,
2323
StreamableComponentResult,
2424
} from 'react-on-rails/types';
25-
import ReactOnRails from 'react-on-rails/ReactOnRails.full';
2625
import handleError from 'react-on-rails/handleError';
2726
import { convertToError } from 'react-on-rails/serverRenderUtils';
27+
import ReactOnRails from './ReactOnRails.full.ts';
2828

2929
import {
3030
streamServerRenderedComponent,
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright (c) 2025 Shakacode LLC
3+
*
4+
* This file is NOT licensed under the MIT (open source) license.
5+
* It is part of the React on Rails Pro offering and is licensed separately.
6+
*
7+
* Unauthorized copying, modification, distribution, or use of this file,
8+
* via any medium, is strictly prohibited without a valid license agreement
9+
* from Shakacode LLC.
10+
*
11+
* For licensing terms, please see:
12+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
13+
*/
14+
15+
import { createBaseClientObject } from 'react-on-rails/@internal/base/client';
16+
import { createBaseFullObject } from 'react-on-rails/@internal/base/full';
17+
import { onPageLoaded, onPageUnloaded } from 'react-on-rails/pageLifecycle';
18+
import { debugTurbolinks } from 'react-on-rails/turbolinksUtils';
19+
import type { Store, StoreGenerator, RegisteredComponent } from 'react-on-rails/types';
20+
import * as ProComponentRegistry from './ComponentRegistry.ts';
21+
import * as ProStoreRegistry from './StoreRegistry.ts';
22+
import {
23+
renderOrHydrateComponent,
24+
hydrateStore,
25+
renderOrHydrateAllComponents,
26+
hydrateAllStores,
27+
renderOrHydrateImmediateHydratedComponents,
28+
hydrateImmediateHydratedStores,
29+
unmountAll,
30+
} from './ClientSideRenderer.ts';
31+
32+
type BaseObjectCreator = typeof createBaseClientObject | typeof createBaseFullObject;
33+
34+
// Pro client startup with immediate hydration support
35+
async function reactOnRailsPageLoaded() {
36+
debugTurbolinks('reactOnRailsPageLoaded [PRO]');
37+
await Promise.all([hydrateAllStores(), renderOrHydrateAllComponents()]);
38+
}
39+
40+
function reactOnRailsPageUnloaded(): void {
41+
debugTurbolinks('reactOnRailsPageUnloaded [PRO]');
42+
unmountAll();
43+
}
44+
45+
function clientStartup() {
46+
if (globalThis.document === undefined) {
47+
return;
48+
}
49+
50+
// eslint-disable-next-line no-underscore-dangle
51+
if (globalThis.__REACT_ON_RAILS_EVENT_HANDLERS_RAN_ONCE__) {
52+
return;
53+
}
54+
55+
// eslint-disable-next-line no-underscore-dangle
56+
globalThis.__REACT_ON_RAILS_EVENT_HANDLERS_RAN_ONCE__ = true;
57+
58+
void renderOrHydrateImmediateHydratedComponents();
59+
void hydrateImmediateHydratedStores();
60+
61+
onPageLoaded(reactOnRailsPageLoaded);
62+
onPageUnloaded(reactOnRailsPageUnloaded);
63+
}
64+
65+
// eslint-disable-next-line import/prefer-default-export
66+
export function createReactOnRailsPro(baseObjectCreator: BaseObjectCreator) {
67+
// Create base object with Pro registries
68+
const baseObject = baseObjectCreator({
69+
ComponentRegistry: ProComponentRegistry,
70+
StoreRegistry: ProStoreRegistry,
71+
});
72+
73+
// Add Pro-specific implementations
74+
const ReactOnRails = {
75+
...baseObject,
76+
77+
// Override client-side rendering stubs with Pro implementations
78+
reactOnRailsPageLoaded(): Promise<void> {
79+
return reactOnRailsPageLoaded();
80+
},
81+
82+
reactOnRailsComponentLoaded(domId: string): Promise<void> {
83+
return renderOrHydrateComponent(domId);
84+
},
85+
86+
// ===================================================================
87+
// PRO-ONLY METHOD IMPLEMENTATIONS
88+
// These methods don't exist in base, add them here
89+
// ===================================================================
90+
91+
getOrWaitForComponent(name: string): Promise<RegisteredComponent> {
92+
return ProComponentRegistry.getOrWaitForComponent(name);
93+
},
94+
95+
getOrWaitForStore(name: string): Promise<Store> {
96+
return ProStoreRegistry.getOrWaitForStore(name);
97+
},
98+
99+
getOrWaitForStoreGenerator(name: string): Promise<StoreGenerator> {
100+
return ProStoreRegistry.getOrWaitForStoreGenerator(name);
101+
},
102+
103+
reactOnRailsStoreLoaded(storeName: string): Promise<void> {
104+
return hydrateStore(storeName);
105+
},
106+
107+
// streamServerRenderedReactComponent is added in ReactOnRails.node.ts
108+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
109+
streamServerRenderedReactComponent(..._args: any[]): any {
110+
throw new Error(
111+
'streamServerRenderedReactComponent requires importing from react-on-rails-pro in Node.js environment',
112+
);
113+
},
114+
115+
// serverRenderRSCReactComponent is added in ReactOnRailsRSC.ts
116+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
117+
serverRenderRSCReactComponent(..._args: any[]): any {
118+
throw new Error('serverRenderRSCReactComponent is supported in RSC bundle only');
119+
},
120+
};
121+
122+
// Assign to global
123+
globalThis.ReactOnRails = ReactOnRails;
124+
125+
// Reset options to defaults
126+
ReactOnRails.resetOptions();
127+
128+
// Run Pro client startup with immediate hydration support
129+
clientStartup();
130+
131+
return ReactOnRails;
132+
}

0 commit comments

Comments
 (0)