react-on-rails
Version:
react-on-rails JavaScript for react_on_rails Ruby gem
207 lines (203 loc) • 9.84 kB
JavaScript
import * as Authenticity from "../Authenticity.js";
import buildConsoleReplay from "../buildConsoleReplay.js";
import reactHydrateOrRender from "../reactHydrateOrRender.js";
import createReactOutput from "../createReactOutput.js";
const DEFAULT_OPTIONS = {
traceTurbolinks: false,
turbo: false,
debugMode: false,
logComponentRegistration: false,
};
// Cache to track created objects and their registries
let cachedObject = null;
let cachedRegistries = null;
export function createBaseClientObject(registries, currentObject = null) {
const { ComponentRegistry, StoreRegistry } = registries;
// Error detection: currentObject is null but we have a cached object
// This indicates webpack misconfiguration (multiple runtime chunks)
if (currentObject === null && cachedObject !== null) {
throw new Error(`\
ReactOnRails was already initialized, but a new initialization was attempted without passing the existing global.
This usually means Webpack's optimization.runtimeChunk is set to "true" or "multiple" instead of "single".
Fix: Set optimization.runtimeChunk to "single" in your webpack configuration.
See: https://github.com/shakacode/react_on_rails/issues/1558`);
}
// Error detection: currentObject exists but doesn't match cached object
// This could indicate:
// 1. Global was contaminated by external code
// 2. Mixing core and pro packages
if (currentObject !== null && cachedObject !== null && currentObject !== cachedObject) {
throw new Error(`\
ReactOnRails global object mismatch detected.
The current global ReactOnRails object is different from the one created by this package.
This usually means:
1. You're mixing react-on-rails (core) with react-on-rails-pro
2. Another library is interfering with the global ReactOnRails object
Fix: Use only one package (core OR pro) consistently throughout your application.`);
}
// Error detection: Different registries with existing cache
// This indicates mixing core and pro packages
if (cachedRegistries !== null) {
if (registries.ComponentRegistry !== cachedRegistries.ComponentRegistry ||
registries.StoreRegistry !== cachedRegistries.StoreRegistry) {
throw new Error(`\
Cannot mix react-on-rails (core) with react-on-rails-pro.
Different registries detected - the packages use incompatible registries.
Fix: Use only react-on-rails OR react-on-rails-pro, not both.`);
}
}
// If we have a cached object, return it (all checks passed above)
if (cachedObject !== null) {
return cachedObject;
}
// Create and return new object
const obj = {
options: {},
isRSCBundle: false,
// ===================================================================
// STABLE METHOD IMPLEMENTATIONS - Core package implementations
// ===================================================================
authenticityToken() {
return Authenticity.authenticityToken();
},
authenticityHeaders(otherHeaders = {}) {
return Authenticity.authenticityHeaders(otherHeaders);
},
reactHydrateOrRender(domNode, reactElement, hydrate) {
return reactHydrateOrRender(domNode, reactElement, hydrate);
},
setOptions(newOptions) {
if (typeof newOptions.traceTurbolinks !== 'undefined') {
this.options.traceTurbolinks = newOptions.traceTurbolinks;
// eslint-disable-next-line no-param-reassign
delete newOptions.traceTurbolinks;
}
if (typeof newOptions.turbo !== 'undefined') {
this.options.turbo = newOptions.turbo;
// eslint-disable-next-line no-param-reassign
delete newOptions.turbo;
}
if (typeof newOptions.debugMode !== 'undefined') {
this.options.debugMode = newOptions.debugMode;
if (newOptions.debugMode) {
console.log('[ReactOnRails] Debug mode enabled');
}
// eslint-disable-next-line no-param-reassign
delete newOptions.debugMode;
}
if (typeof newOptions.logComponentRegistration !== 'undefined') {
this.options.logComponentRegistration = newOptions.logComponentRegistration;
if (newOptions.logComponentRegistration) {
console.log('[ReactOnRails] Component registration logging enabled');
}
// eslint-disable-next-line no-param-reassign
delete newOptions.logComponentRegistration;
}
if (Object.keys(newOptions).length > 0) {
throw new Error(`Invalid options passed to ReactOnRails.options: ${JSON.stringify(newOptions)}`);
}
},
option(key) {
return this.options[key];
},
buildConsoleReplay() {
return buildConsoleReplay();
},
resetOptions() {
this.options = { ...DEFAULT_OPTIONS };
},
// ===================================================================
// REGISTRY METHOD IMPLEMENTATIONS - Using provided registries
// ===================================================================
register(components) {
if (this.options.debugMode || this.options.logComponentRegistration) {
// Use performance.now() if available, otherwise fallback to Date.now()
const perf = typeof performance !== 'undefined' ? performance : { now: () => Date.now() };
const startTime = perf.now();
const componentNames = Object.keys(components);
console.log(`[ReactOnRails] Registering ${componentNames.length} component(s): ${componentNames.join(', ')}`);
ComponentRegistry.register(components);
const endTime = perf.now();
console.log(`[ReactOnRails] Component registration completed in ${(endTime - startTime).toFixed(2)}ms`);
// Log individual component details if in full debug mode
if (this.options.debugMode) {
componentNames.forEach((name) => {
const component = components[name];
const size = component.toString().length;
console.log(`[ReactOnRails] ✅ Registered: ${name} (${size} chars)`);
});
}
}
else {
ComponentRegistry.register(components);
}
},
registerStore(stores) {
this.registerStoreGenerators(stores);
},
registerStoreGenerators(storeGenerators) {
if (!storeGenerators) {
throw new Error('Called ReactOnRails.registerStoreGenerators with a null or undefined, rather than ' +
'an Object with keys being the store names and the values are the store generators.');
}
StoreRegistry.register(storeGenerators);
},
getStore(name, throwIfMissing = true) {
return StoreRegistry.getStore(name, throwIfMissing);
},
getStoreGenerator(name) {
return StoreRegistry.getStoreGenerator(name);
},
setStore(name, store) {
StoreRegistry.setStore(name, store);
},
clearHydratedStores() {
StoreRegistry.clearHydratedStores();
},
getComponent(name) {
return ComponentRegistry.get(name);
},
registeredComponents() {
return ComponentRegistry.components();
},
storeGenerators() {
return StoreRegistry.storeGenerators();
},
stores() {
return StoreRegistry.stores();
},
render(name, props, domNodeId, hydrate) {
const componentObj = ComponentRegistry.get(name);
const reactElement = createReactOutput({ componentObj, props, domNodeId });
return this.reactHydrateOrRender(document.getElementById(domNodeId), reactElement, hydrate);
},
// ===================================================================
// CLIENT-SIDE RENDERING STUBS - To be overridden by createReactOnRails
// ===================================================================
reactOnRailsPageLoaded() {
throw new Error('ReactOnRails.reactOnRailsPageLoaded is not initialized. This is a bug in react-on-rails.');
},
reactOnRailsComponentLoaded(domId) {
void domId; // Mark as used
throw new Error('ReactOnRails.reactOnRailsComponentLoaded is not initialized. This is a bug in react-on-rails.');
},
// ===================================================================
// SSR STUBS - Will throw errors in client bundle, overridden in full
// ===================================================================
// eslint-disable-next-line @typescript-eslint/no-explicit-any
serverRenderReactComponent(...args) {
void args; // Mark as used
throw new Error('serverRenderReactComponent is not available in "react-on-rails/client". Import "react-on-rails" server-side.');
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
handleError(...args) {
void args; // Mark as used
throw new Error('handleError is not available in "react-on-rails/client". Import "react-on-rails" server-side.');
},
};
// Cache the object and registries
cachedObject = obj;
cachedRegistries = registries;
return obj;
}
//# sourceMappingURL=client.js.map