diff --git a/docs/api/javascript-api.md b/docs/api/javascript-api.md index e17d9cfc3..20e9cf300 100644 --- a/docs/api/javascript-api.md +++ b/docs/api/javascript-api.md @@ -5,7 +5,7 @@ Rails has built-in protection for Cross-Site Request Forgery (CSRF), see [Rails Documentation](http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf). To nicely utilize this feature in JavaScript requests, React on Rails provides two helpers that can be used as following for POST, PUT or DELETE requests: ```js -import ReactOnRails from 'react-on-rails'; +import * as ReactOnRails from 'react-on-rails'; // reads from DOM csrf token generated by Rails in <%= csrf_meta_tags %> csrfToken = ReactOnRails.authenticityToken(); diff --git a/docs/getting-started.md b/docs/getting-started.md index 299094b33..b6683d446 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -90,9 +90,9 @@ issue. - The `component_name` parameter is a string matching the name you used to expose your React component globally. So, in the above examples, if you had a React component named "HelloWorld", you would register it with the following lines: ```js - import ReactOnRails from 'react-on-rails'; + import { register } from 'react-on-rails'; import HelloWorld from './HelloWorld'; - ReactOnRails.register({ HelloWorld }); + register({ HelloWorld }); ``` Exposing your component in this way allows you to reference the component from a Rails view. You can expose as many components as you like, but their names must be unique. See below for the details of how you expose your components via the React on Rails Webpack configuration. You may call `ReactOnRails.register` many times. @@ -133,8 +133,8 @@ This is how to expose a component to the `react_component` view helper. ```javascript // app/javascript/packs/hello-world-bundle.js import HelloWorld from '../components/HelloWorld'; -import ReactOnRails from 'react-on-rails'; -ReactOnRails.register({ HelloWorld }); +import { register } from 'react-on-rails'; +register({ HelloWorld }); ``` #### Different Server-Side Rendering Code (and a Server-Specific Bundle) diff --git a/docs/guides/file-system-based-automated-bundle-generation.md b/docs/guides/file-system-based-automated-bundle-generation.md index fed6fa0c4..4f65e38fc 100644 --- a/docs/guides/file-system-based-automated-bundle-generation.md +++ b/docs/guides/file-system-based-automated-bundle-generation.md @@ -103,15 +103,15 @@ app/javascript: └── logo.svg ``` -Previously, many applications would use one pack (webpack entrypoint) for many components. In this example, the`application.js` file manually registers server components, `FooComponentOne`, `BarComponentOne` and `BarComponentTwo`. +Previously, many applications would use one pack (webpack entrypoint) for many components. In this example, the `application.js` file manually registers server components, `FooComponentOne`, `BarComponentOne` and `BarComponentTwo`. ```jsx -import ReactOnRails from 'react-on-rails'; +import { register } from 'react-on-rails'; import FooComponentOne from '../src/Foo/FooComponentOne'; import BarComponentOne from '../src/Foo/BarComponentOne'; import BarComponentTwo from '../src/Foo/BarComponentTwo'; -ReactOnRails.register({ FooComponentOne, BarComponentOne, BarComponentTwo }); +register({ FooComponentOne, BarComponentOne, BarComponentTwo }); ``` Your layout would contain: diff --git a/docs/guides/how-to-use-different-files-for-client-and-server-rendering.md b/docs/guides/how-to-use-different-files-for-client-and-server-rendering.md index 4e347c83e..b60b3ab7f 100644 --- a/docs/guides/how-to-use-different-files-for-client-and-server-rendering.md +++ b/docs/guides/how-to-use-different-files-for-client-and-server-rendering.md @@ -9,17 +9,17 @@ Many projects will have different entry points for client and server rendering. Your Client Entry can look like this: ```js -import ReactOnRails from 'react-on-rails/client'; +import { register } from 'react-on-rails/client'; import App from './ClientApp'; -ReactOnRails.register({ App }); +register({ App }); ``` So your Server Entry can look like: ```js -import ReactOnRails from 'react-on-rails'; +import { register } from 'react-on-rails'; import App from './ServerApp'; -ReactOnRails.register({ App }); +register({ App }); ``` Note that the only difference is in the imports. diff --git a/docs/javascript/code-splitting.md b/docs/javascript/code-splitting.md index c33e7e93d..8c1c4e880 100644 --- a/docs/javascript/code-splitting.md +++ b/docs/javascript/code-splitting.md @@ -39,7 +39,7 @@ Here's an example of how you might use this in practice: #### clientRegistration.js ```js -import ReactOnRails from 'react-on-rails/client'; +import * as ReactOnRails from 'react-on-rails/client'; import NavigationApp from './NavigationApp'; // Note that we're importing a different RouterApp than in serverRegistration.js @@ -57,7 +57,7 @@ ReactOnRails.register({ #### serverRegistration.js ```js -import ReactOnRails from 'react-on-rails'; +import * as ReactOnRails from 'react-on-rails'; import NavigationApp from './NavigationApp'; // Note that we're importing a different RouterApp than in clientRegistration.js @@ -76,7 +76,7 @@ Note that you should not register a renderer on the server, since there won't be #### RouterAppRenderer.jsx ```jsx -import ReactOnRails from 'react-on-rails/client'; +import * as ReactOnRails from 'react-on-rails/client'; import React from 'react'; import ReactDOM from 'react-dom'; import Router from 'react-router/lib/Router'; diff --git a/docs/javascript/react-router.md b/docs/javascript/react-router.md index 3cae7cbdf..0af04d009 100644 --- a/docs/javascript/react-router.md +++ b/docs/javascript/react-router.md @@ -46,7 +46,7 @@ import React from 'react'; import { renderToString } from 'react-dom/server'; import { StaticRouter } from 'react-router'; import { Provider } from 'react-redux'; -import ReactOnRails from 'react-on-rails'; +import * as ReactOnRails from 'react-on-rails'; // App.jsx from src/client/App.jsx import App from '../App'; diff --git a/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/registration.js.tt b/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/registration.js.tt index d20e720f2..9a61949e5 100644 --- a/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/registration.js.tt +++ b/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/registration.js.tt @@ -1,8 +1,8 @@ -import ReactOnRails from 'react-on-rails/client'; +import { register } from 'react-on-rails/client'; import <%= config[:component_name] %> from '<%= config[:app_relative_path] %>'; // This is how react_on_rails can see the HelloWorld in the browser. -ReactOnRails.register({ +register({ <%= config[:component_name] %>, }); diff --git a/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/server-bundle.js b/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/server-bundle.js index 7d764f113..20ec58dad 100644 --- a/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/server-bundle.js +++ b/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/server-bundle.js @@ -1,8 +1,8 @@ -import ReactOnRails from 'react-on-rails'; +import { register } from 'react-on-rails'; import HelloWorld from '../bundles/HelloWorld/components/HelloWorldServer'; // This is how react_on_rails can see the HelloWorld in the browser. -ReactOnRails.register({ +register({ HelloWorld, }); diff --git a/lib/react_on_rails/packs_generator.rb b/lib/react_on_rails/packs_generator.rb index ae4169162..046542fea 100644 --- a/lib/react_on_rails/packs_generator.rb +++ b/lib/react_on_rails/packs_generator.rb @@ -107,7 +107,7 @@ def pack_file_contents(file_path) relative_component_path = relative_component_path_from_generated_pack(file_path) <<~FILE_CONTENT.strip - import ReactOnRails from 'react-on-rails/client'; + import * as ReactOnRails from 'react-on-rails/client'; import #{registered_component_name} from '#{relative_component_path}'; ReactOnRails.register({#{registered_component_name}}); @@ -123,7 +123,8 @@ def create_server_pack def build_server_pack_content(component_on_server_imports, server_components, client_components) content = <<~FILE_CONTENT - import ReactOnRails from 'react-on-rails'; + import * as ReactOnRails from 'react-on-rails'; + globalThis.ReactOnRails = ReactOnRails; #{component_on_server_imports.join("\n")}\n FILE_CONTENT diff --git a/node_package/src/ReactOnRails.client.ts b/node_package/src/ReactOnRails.client.ts index 97d4e1078..0f4e09ce2 100644 --- a/node_package/src/ReactOnRails.client.ts +++ b/node_package/src/ReactOnRails.client.ts @@ -3,7 +3,6 @@ import * as ClientStartup from './clientStartup.ts'; import { renderOrHydrateComponent, hydrateStore } from './ClientSideRenderer.ts'; import * as ComponentRegistry from './ComponentRegistry.ts'; import * as StoreRegistry from './StoreRegistry.ts'; -import buildConsoleReplay from './buildConsoleReplay.ts'; import createReactOutput from './createReactOutput.ts'; import * as Authenticity from './Authenticity.ts'; import type { @@ -14,185 +13,168 @@ import type { AuthenticityHeaders, Store, StoreGenerator, - ReactOnRailsOptions, } from './types/index.ts'; -import reactHydrateOrRender from './reactHydrateOrRender.ts'; +import reactHydrateOrRenderInternal from './reactHydrateOrRender.ts'; +import { resetOptions } from './options.ts'; -if (globalThis.ReactOnRails !== undefined) { +export { default as buildConsoleReplay } from './buildConsoleReplay.ts'; + +declare global { + /* eslint-disable no-var,vars-on-top,no-underscore-dangle */ + var __REACT_ON_RAILS_LOADED__: boolean; + /* eslint-enable no-var,vars-on-top,no-underscore-dangle */ +} + +// eslint-disable-next-line no-underscore-dangle +if (globalThis.__REACT_ON_RAILS_LOADED__) { throw new Error(`\ The ReactOnRails value exists in the ${globalThis} scope, it may not be safe to overwrite it. This could be caused by setting Webpack's optimization.runtimeChunk to "true" or "multiple," rather than "single." Check your Webpack configuration. Read more at https://github.com/shakacode/react_on_rails/issues/1558.`); } -const DEFAULT_OPTIONS = { - traceTurbolinks: false, - turbo: false, -}; - -globalThis.ReactOnRails = { - options: {}, - - register(components: Record): void { - ComponentRegistry.register(components); - }, - - registerStore(stores: Record): void { - this.registerStoreGenerators(stores); - }, +// eslint-disable-next-line no-underscore-dangle +globalThis.__REACT_ON_RAILS_LOADED__ = true; - registerStoreGenerators(storeGenerators: Record): void { - 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: string, throwIfMissing = true): Store | undefined { - return StoreRegistry.getStore(name, throwIfMissing); - }, - - getOrWaitForStore(name: string): Promise { - return StoreRegistry.getOrWaitForStore(name); - }, - - getOrWaitForStoreGenerator(name: string): Promise { - return StoreRegistry.getOrWaitForStoreGenerator(name); - }, - - reactHydrateOrRender(domNode: Element, reactElement: ReactElement, hydrate: boolean): RenderReturnType { - return reactHydrateOrRender(domNode, reactElement, hydrate); - }, +// TODO: convert to re-exports if everything works fine +export function register(components: Record): void { + ComponentRegistry.register(components); +} - setOptions(newOptions: Partial): void { - if (typeof newOptions.traceTurbolinks !== 'undefined') { - this.options.traceTurbolinks = newOptions.traceTurbolinks; +// eslint-disable-next-line @typescript-eslint/no-shadow +export function registerStoreGenerators(storeGenerators: Record): void { + 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.', + ); + } - // eslint-disable-next-line no-param-reassign - delete newOptions.traceTurbolinks; - } + StoreRegistry.register(storeGenerators); +} - if (typeof newOptions.turbo !== 'undefined') { - this.options.turbo = newOptions.turbo; +// eslint-disable-next-line @typescript-eslint/no-shadow +export function registerStore(stores: Record): void { + registerStoreGenerators(stores); +} - // eslint-disable-next-line no-param-reassign - delete newOptions.turbo; - } +export function getStore(name: string, throwIfMissing = true): Store | undefined { + return StoreRegistry.getStore(name, throwIfMissing); +} - if (Object.keys(newOptions).length > 0) { - throw new Error(`Invalid options passed to ReactOnRails.options: ${JSON.stringify(newOptions)}`); - } - }, +export function getOrWaitForStore(name: string): Promise { + return StoreRegistry.getOrWaitForStore(name); +} - reactOnRailsPageLoaded() { - return ClientStartup.reactOnRailsPageLoaded(); - }, +export function getOrWaitForStoreGenerator(name: string): Promise { + return StoreRegistry.getOrWaitForStoreGenerator(name); +} - reactOnRailsComponentLoaded(domId: string): Promise { - return renderOrHydrateComponent(domId); - }, +export function reactHydrateOrRender( + domNode: Element, + reactElement: ReactElement, + hydrate: boolean, +): RenderReturnType { + return reactHydrateOrRenderInternal(domNode, reactElement, hydrate); +} - reactOnRailsStoreLoaded(storeName: string): Promise { - return hydrateStore(storeName); - }, +export function reactOnRailsPageLoaded() { + return ClientStartup.reactOnRailsPageLoaded(); +} - authenticityToken(): string | null { - return Authenticity.authenticityToken(); - }, +export function reactOnRailsComponentLoaded(domId: string): Promise { + return renderOrHydrateComponent(domId); +} - authenticityHeaders(otherHeaders: Record = {}): AuthenticityHeaders { - return Authenticity.authenticityHeaders(otherHeaders); - }, +export function reactOnRailsStoreLoaded(storeName: string): Promise { + return hydrateStore(storeName); +} - // ///////////////////////////////////////////////////////////////////////////// - // INTERNALLY USED APIs - // ///////////////////////////////////////////////////////////////////////////// +export function authenticityToken(): string | null { + return Authenticity.authenticityToken(); +} - option(key: K): ReactOnRailsOptions[K] | undefined { - return this.options[key]; - }, +export function authenticityHeaders(otherHeaders: Record = {}): AuthenticityHeaders { + return Authenticity.authenticityHeaders(otherHeaders); +} - getStoreGenerator(name: string): StoreGenerator { - return StoreRegistry.getStoreGenerator(name); - }, +// ///////////////////////////////////////////////////////////////////////////// +// INTERNALLY USED APIs +// ///////////////////////////////////////////////////////////////////////////// - setStore(name: string, store: Store): void { - StoreRegistry.setStore(name, store); - }, +export function getStoreGenerator(name: string): StoreGenerator { + return StoreRegistry.getStoreGenerator(name); +} - clearHydratedStores(): void { - StoreRegistry.clearHydratedStores(); - }, - - render(name: string, props: Record, domNodeId: string, hydrate: boolean): RenderReturnType { - const componentObj = ComponentRegistry.get(name); - const reactElement = createReactOutput({ componentObj, props, domNodeId }); - - return reactHydrateOrRender( - document.getElementById(domNodeId) as Element, - reactElement as ReactElement, - hydrate, - ); - }, +export function setStore(name: string, store: Store): void { + StoreRegistry.setStore(name, store); +} - getComponent(name: string): RegisteredComponent { - return ComponentRegistry.get(name); - }, +export function clearHydratedStores(): void { + StoreRegistry.clearHydratedStores(); +} - getOrWaitForComponent(name: string): Promise { - return ComponentRegistry.getOrWaitForComponent(name); - }, +export function render( + name: string, + props: Record, + domNodeId: string, + hydrate: boolean, +): RenderReturnType { + const componentObj = ComponentRegistry.get(name); + const reactElement = createReactOutput({ componentObj, props, domNodeId }); + + return reactHydrateOrRenderInternal( + document.getElementById(domNodeId) as Element, + reactElement as ReactElement, + hydrate, + ); +} - serverRenderReactComponent(): null | string | Promise { - throw new Error( - 'serverRenderReactComponent is not available in "react-on-rails/client". Import "react-on-rails" server-side.', - ); - }, +export function getComponent(name: string): RegisteredComponent { + return ComponentRegistry.get(name); +} - streamServerRenderedReactComponent() { - throw new Error( - 'streamServerRenderedReactComponent is only supported when using a bundle built for Node.js environments', - ); - }, +export function getOrWaitForComponent(name: string): Promise { + return ComponentRegistry.getOrWaitForComponent(name); +} - serverRenderRSCReactComponent() { - throw new Error('serverRenderRSCReactComponent is supported in RSC bundle only.'); - }, +export function serverRenderReactComponent(): null | string | Promise { + throw new Error( + 'serverRenderReactComponent is not available in "react-on-rails/client". Import "react-on-rails" server-side.', + ); +} - handleError(): string | undefined { - throw new Error( - 'handleError is not available in "react-on-rails/client". Import "react-on-rails" server-side.', - ); - }, +export function streamServerRenderedReactComponent() { + throw new Error( + 'streamServerRenderedReactComponent is only supported when using a bundle built for Node.js environments', + ); +} - buildConsoleReplay(): string { - return buildConsoleReplay(); - }, +export function serverRenderRSCReactComponent() { + throw new Error('serverRenderRSCReactComponent is supported in RSC bundle only.'); +} - registeredComponents(): Map { - return ComponentRegistry.components(); - }, +export function handleError(): string | undefined { + throw new Error( + 'handleError is not available in "react-on-rails/client". Import "react-on-rails" server-side.', + ); +} - storeGenerators(): Map { - return StoreRegistry.storeGenerators(); - }, +export function registeredComponents(): Map { + return ComponentRegistry.components(); +} - stores(): Map { - return StoreRegistry.stores(); - }, +export function storeGenerators(): Map { + return StoreRegistry.storeGenerators(); +} - resetOptions(): void { - this.options = { ...DEFAULT_OPTIONS }; - }, -}; +export function stores(): Map { + return StoreRegistry.stores(); +} -globalThis.ReactOnRails.resetOptions(); +resetOptions(); ClientStartup.clientStartup(); export * from './types/index.ts'; -export default globalThis.ReactOnRails; +export * from './options.ts'; diff --git a/node_package/src/ReactOnRails.full.ts b/node_package/src/ReactOnRails.full.ts index 4f03bfb53..d8a25ff8c 100644 --- a/node_package/src/ReactOnRails.full.ts +++ b/node_package/src/ReactOnRails.full.ts @@ -1,9 +1,3 @@ -import handleError from './handleError.ts'; -import serverRenderReactComponent from './serverRenderReactComponent.ts'; -import type { RenderParams, RenderResult, ErrorOptions } from './types/index.ts'; - -import Client from './ReactOnRails.client.ts'; - if (typeof window !== 'undefined') { // warn to include a collapsed stack trace console.warn( @@ -11,10 +5,6 @@ if (typeof window !== 'undefined') { ); } -Client.handleError = (options: ErrorOptions): string | undefined => handleError(options); - -Client.serverRenderReactComponent = (options: RenderParams): null | string | Promise => - serverRenderReactComponent(options); - -export * from './types/index.ts'; -export default Client; +export * from './ReactOnRails.client.ts'; +export { default as handleError } from './handleError.ts'; +export { default as serverRenderReactComponent } from './serverRenderReactComponent.ts'; diff --git a/node_package/src/ReactOnRails.node.ts b/node_package/src/ReactOnRails.node.ts index 407d2658b..3b3fdf2da 100644 --- a/node_package/src/ReactOnRails.node.ts +++ b/node_package/src/ReactOnRails.node.ts @@ -1,8 +1,2 @@ -import ReactOnRails from './ReactOnRails.full.ts'; -import streamServerRenderedReactComponent from './streamServerRenderedReactComponent.ts'; - -ReactOnRails.streamServerRenderedReactComponent = streamServerRenderedReactComponent; - export * from './ReactOnRails.full.ts'; -// eslint-disable-next-line no-restricted-exports -- see https://github.com/eslint/eslint/issues/15617 -export { default } from './ReactOnRails.full.ts'; +export { default as streamServerRenderedReactComponent } from './streamServerRenderedReactComponent.ts'; diff --git a/node_package/src/ReactOnRailsRSC.ts b/node_package/src/ReactOnRailsRSC.ts index 428787eab..c96a265c2 100644 --- a/node_package/src/ReactOnRailsRSC.ts +++ b/node_package/src/ReactOnRailsRSC.ts @@ -2,7 +2,6 @@ import { renderToPipeableStream } from 'react-on-rails-rsc/server.node'; import { PassThrough, Readable } from 'stream'; import { RSCRenderParams, StreamRenderState, StreamableComponentResult } from './types/index.ts'; -import ReactOnRails from './ReactOnRails.full.ts'; import buildConsoleReplay from './buildConsoleReplay.ts'; import handleError from './handleError.ts'; import { convertToError, createResultObject } from './serverRenderUtils.ts'; @@ -59,13 +58,12 @@ const streamRenderRSCComponent = ( return readableStream; }; -ReactOnRails.serverRenderRSCReactComponent = (options: RSCRenderParams) => { +export * from './ReactOnRails.full.ts'; + +export const serverRenderRSCReactComponent = (options: RSCRenderParams) => { try { return streamServerRenderedComponent(options, streamRenderRSCComponent); } finally { console.history = []; } }; - -export * from './types/index.ts'; -export default ReactOnRails; diff --git a/node_package/src/clientStartup.ts b/node_package/src/clientStartup.ts index bffefaa3a..45634d2f5 100644 --- a/node_package/src/clientStartup.ts +++ b/node_package/src/clientStartup.ts @@ -8,6 +8,12 @@ import { import { onPageLoaded, onPageUnloaded } from './pageLifecycle.ts'; import { debugTurbolinks } from './turbolinksUtils.ts'; +declare global { + /* eslint-disable no-var,vars-on-top,no-underscore-dangle */ + var __REACT_ON_RAILS_EVENT_HANDLERS_RAN_ONCE__: boolean; + /* eslint-enable no-var,vars-on-top,no-underscore-dangle */ +} + export async function reactOnRailsPageLoaded() { debugTurbolinks('reactOnRailsPageLoaded'); await Promise.all([hydrateAllStores(), renderOrHydrateAllComponents()]); diff --git a/node_package/src/context.ts b/node_package/src/context.ts index 8d5485cf2..7930b71fe 100644 --- a/node_package/src/context.ts +++ b/node_package/src/context.ts @@ -1,11 +1,4 @@ -import type { ReactOnRailsInternal, RailsContext } from './types/index.ts'; - -declare global { - /* eslint-disable no-var,vars-on-top,no-underscore-dangle */ - var ReactOnRails: ReactOnRailsInternal; - var __REACT_ON_RAILS_EVENT_HANDLERS_RAN_ONCE__: boolean; - /* eslint-enable no-var,vars-on-top,no-underscore-dangle */ -} +import type { RailsContext } from './types/index.ts'; let currentRailsContext: RailsContext | null = null; diff --git a/node_package/src/options.ts b/node_package/src/options.ts new file mode 100644 index 000000000..af3fc87ba --- /dev/null +++ b/node_package/src/options.ts @@ -0,0 +1,36 @@ +import type { ReactOnRailsOptions } from './types/index.ts'; + +const DEFAULT_OPTIONS = { + traceTurbolinks: false, + turbo: false, +}; + +let options: ReactOnRailsOptions = {}; + +export function setOptions(newOptions: Partial): void { + if (typeof newOptions.traceTurbolinks !== 'undefined') { + options.traceTurbolinks = newOptions.traceTurbolinks; + + // eslint-disable-next-line no-param-reassign + delete newOptions.traceTurbolinks; + } + + if (typeof newOptions.turbo !== 'undefined') { + options.turbo = newOptions.turbo; + + // eslint-disable-next-line no-param-reassign + delete newOptions.turbo; + } + + if (Object.keys(newOptions).length > 0) { + throw new Error(`Invalid options passed to ReactOnRails.options: ${JSON.stringify(newOptions)}`); + } +} + +export function option(key: K): ReactOnRailsOptions[K] | undefined { + return options[key]; +} + +export function resetOptions(): void { + options = { ...DEFAULT_OPTIONS }; +} diff --git a/node_package/src/registerServerComponent/client.ts b/node_package/src/registerServerComponent/client.ts index 6a1b62a61..76afa70e1 100644 --- a/node_package/src/registerServerComponent/client.ts +++ b/node_package/src/registerServerComponent/client.ts @@ -1,4 +1,4 @@ -import ReactOnRails from '../ReactOnRails.client.ts'; +import { register } from '../ReactOnRails.client.ts'; import RSCClientRoot from '../RSCClientRoot.ts'; import { RegisterServerComponentOptions, @@ -56,7 +56,7 @@ const registerServerComponent = (options: RegisterServerComponentOptions, ...com domNodeId, ); } - ReactOnRails.register(componentsWrappedInRSCClientRoot); + register(componentsWrappedInRSCClientRoot); }; export default registerServerComponent; diff --git a/node_package/src/registerServerComponent/server.ts b/node_package/src/registerServerComponent/server.ts index 40d68a1da..b877b33d6 100644 --- a/node_package/src/registerServerComponent/server.ts +++ b/node_package/src/registerServerComponent/server.ts @@ -1,4 +1,4 @@ -import ReactOnRails from '../ReactOnRails.client.ts'; +import { register } from '../ReactOnRails.client.ts'; import { ReactComponent } from '../types/index.ts'; /** @@ -31,7 +31,7 @@ import { ReactComponent } from '../types/index.ts'; * ``` */ const registerServerComponent = (components: Record) => { - ReactOnRails.register(components); + register(components); }; export default registerServerComponent; diff --git a/node_package/src/turbolinksUtils.ts b/node_package/src/turbolinksUtils.ts index 4ec70415b..eeca843c1 100644 --- a/node_package/src/turbolinksUtils.ts +++ b/node_package/src/turbolinksUtils.ts @@ -1,3 +1,5 @@ +import { option } from './options.ts'; + declare global { namespace Turbolinks { interface TurbolinksStatic { @@ -16,7 +18,7 @@ export function debugTurbolinks(...msg: unknown[]): void { return; } - if (globalThis.ReactOnRails?.option('traceTurbolinks')) { + if (option('traceTurbolinks')) { console.log('TURBO:', ...msg); } } @@ -26,7 +28,7 @@ export function turbolinksInstalled(): boolean { } export function turboInstalled() { - return globalThis.ReactOnRails?.option('turbo') === true; + return option('turbo') === true; } export function turbolinksVersion5(): boolean { diff --git a/node_package/tests/Authenticity.test.js b/node_package/tests/Authenticity.test.js index 68d97edaa..e3e6f249b 100644 --- a/node_package/tests/Authenticity.test.js +++ b/node_package/tests/Authenticity.test.js @@ -1,4 +1,4 @@ -import ReactOnRails from '../src/ReactOnRails.client.ts'; +import * as ReactOnRails from '../src/ReactOnRails.client.ts'; const testToken = 'TEST_CSRF_TOKEN'; diff --git a/node_package/tests/ReactOnRails.test.jsx b/node_package/tests/ReactOnRails.test.jsx index 3e7f757ad..1be7450f1 100644 --- a/node_package/tests/ReactOnRails.test.jsx +++ b/node_package/tests/ReactOnRails.test.jsx @@ -4,7 +4,7 @@ import { createStore } from 'redux'; import * as React from 'react'; import * as createReactClass from 'create-react-class'; -import ReactOnRails from '../src/ReactOnRails.client.ts'; +import * as ReactOnRails from '../src/ReactOnRails.client.ts'; describe('ReactOnRails', () => { expect.assertions(14); diff --git a/spec/dummy/app/views/pages/client_side_hello_world.html.erb b/spec/dummy/app/views/pages/client_side_hello_world.html.erb index 06e291d79..3a4c084a2 100644 --- a/spec/dummy/app/views/pages/client_side_hello_world.html.erb +++ b/spec/dummy/app/views/pages/client_side_hello_world.html.erb @@ -22,8 +22,8 @@
       import HelloWorld from '../components/HelloWorld';
-      import ReactOnRails from 'react-on-rails/client';
-      ReactOnRails.register({ HelloWorld });
+      import { register } from 'react-on-rails/client';
+      register({ HelloWorld });
     
  • diff --git a/spec/dummy/app/views/pages/server_side_hello_world.html.erb b/spec/dummy/app/views/pages/server_side_hello_world.html.erb index 8dcb1ea96..d2f602890 100644 --- a/spec/dummy/app/views/pages/server_side_hello_world.html.erb +++ b/spec/dummy/app/views/pages/server_side_hello_world.html.erb @@ -48,8 +48,8 @@
           import HelloWorld from '../components/HelloWorld';
    -      import ReactOnRails from 'react-on-rails/client';
    -      ReactOnRails.register({ HelloWorld });
    +      import { register } from 'react-on-rails/client';
    +      register({ HelloWorld });
         
  • diff --git a/spec/dummy/app/views/pages/server_side_redux_app.html.erb b/spec/dummy/app/views/pages/server_side_redux_app.html.erb index b11ad675f..246a41c0c 100644 --- a/spec/dummy/app/views/pages/server_side_redux_app.html.erb +++ b/spec/dummy/app/views/pages/server_side_redux_app.html.erb @@ -37,8 +37,8 @@
         import ReduxApp from './ClientReduxApp';
    -    import ReactOnRails from 'react-on-rails/client';
    -    ReactOnRails.register({ ReduxApp });
    +    import { register } from 'react-on-rails/client';
    +    register({ ReduxApp });
         
  • diff --git a/spec/dummy/app/views/pages/server_side_redux_app_cached.html.erb b/spec/dummy/app/views/pages/server_side_redux_app_cached.html.erb index dec8fb17d..5b2e5f0c3 100644 --- a/spec/dummy/app/views/pages/server_side_redux_app_cached.html.erb +++ b/spec/dummy/app/views/pages/server_side_redux_app_cached.html.erb @@ -43,7 +43,7 @@
         import ReduxApp from './ClientReduxApp';
    -    import ReactOnRails from 'react-on-rails/client';
    +    import * as ReactOnRails from 'react-on-rails/client';
         ReactOnRails.register({ ReduxApp });
         
  • diff --git a/spec/dummy/app/views/pages/xhr_refresh.html.erb b/spec/dummy/app/views/pages/xhr_refresh.html.erb index fdad0ee63..df2eb1252 100644 --- a/spec/dummy/app/views/pages/xhr_refresh.html.erb +++ b/spec/dummy/app/views/pages/xhr_refresh.html.erb @@ -32,8 +32,8 @@
           import HellowWorldRehydratable from '../components/HellowWorldRehydratable';
    -      import ReactOnRails from 'react-on-rails/client';
    -      ReactOnRails.register({ HellowWorldRehydratable });
    +      import { register } from 'react-on-rails/client';
    +      register({ HellowWorldRehydratable });
         
  • diff --git a/spec/dummy/client/app/packs/client-bundle.js b/spec/dummy/client/app/packs/client-bundle.js index 93016fe4a..07140a012 100644 --- a/spec/dummy/client/app/packs/client-bundle.js +++ b/spec/dummy/client/app/packs/client-bundle.js @@ -4,7 +4,7 @@ import 'jquery'; import 'jquery-ujs'; import '@hotwired/turbo-rails'; -import ReactOnRails from 'react-on-rails/client'; +import * as ReactOnRails from 'react-on-rails/client'; import HelloTurboStream from '../startup/HelloTurboStream'; import SharedReduxStore from '../stores/SharedReduxStore'; diff --git a/spec/dummy/client/app/packs/server-bundle.js b/spec/dummy/client/app/packs/server-bundle.js index ac246c2ed..ca0facc87 100644 --- a/spec/dummy/client/app/packs/server-bundle.js +++ b/spec/dummy/client/app/packs/server-bundle.js @@ -1,7 +1,7 @@ // import statement added by react_on_rails:generate_packs rake task import './../generated/server-bundle-generated.js'; // eslint-disable-line import/extensions // Shows the mapping from the exported object to the name used by the server rendering. -import ReactOnRails from 'react-on-rails'; +import * as ReactOnRails from 'react-on-rails'; // Example of server rendering with no React import HelloString from '../non_react/HelloString'; diff --git a/spec/dummy/client/app/startup/HelloWorldRehydratable.jsx b/spec/dummy/client/app/startup/HelloWorldRehydratable.jsx index 2b894ec39..f1cc4e643 100644 --- a/spec/dummy/client/app/startup/HelloWorldRehydratable.jsx +++ b/spec/dummy/client/app/startup/HelloWorldRehydratable.jsx @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import ReactOnRails from 'react-on-rails/client'; +import { render } from 'react-on-rails/client'; import RailsContext from '../components/RailsContext'; class HelloWorldRehydratable extends React.Component { @@ -51,7 +51,7 @@ class HelloWorldRehydratable extends React.Component { // Read props from the component specification tag and merge railsContext const mergedProps = { ...JSON.parse(componentSpecificationTag.textContent), railsContext }; // Hydrate - ReactOnRails.render(registeredComponentName, mergedProps, component.id, true); + render(registeredComponentName, mergedProps, component.id, true); } } diff --git a/spec/dummy/client/app/startup/ReduxSharedStoreApp.client.jsx b/spec/dummy/client/app/startup/ReduxSharedStoreApp.client.jsx index ba0bd726b..b7b2cb90c 100644 --- a/spec/dummy/client/app/startup/ReduxSharedStoreApp.client.jsx +++ b/spec/dummy/client/app/startup/ReduxSharedStoreApp.client.jsx @@ -3,7 +3,7 @@ import React from 'react'; import { Provider } from 'react-redux'; -import ReactOnRails from 'react-on-rails/client'; +import { getStore } from 'react-on-rails/client'; import ReactDOMClient from 'react-dom/client'; import HelloWorldContainer from '../components/HelloWorldContainer'; @@ -24,7 +24,7 @@ export default (props, _railsContext, domNodeId) => { delete props.prerender; // This is where we get the existing store. - const store = ReactOnRails.getStore('SharedReduxStore'); + const store = getStore('SharedReduxStore'); // renderApp is a function required for hot reloading. see // https://github.com/retroalgic/react-on-rails-hot-minimal/blob/master/client/src/entry.js diff --git a/spec/dummy/client/app/startup/ReduxSharedStoreApp.server.jsx b/spec/dummy/client/app/startup/ReduxSharedStoreApp.server.jsx index 5982ab441..23619588f 100644 --- a/spec/dummy/client/app/startup/ReduxSharedStoreApp.server.jsx +++ b/spec/dummy/client/app/startup/ReduxSharedStoreApp.server.jsx @@ -2,7 +2,7 @@ // Compare this to the ./ReduxSharedStoreApp.client.jsx file which is used for client side rendering. import React from 'react'; -import ReactOnRails from 'react-on-rails'; +import { getStore } from 'react-on-rails'; import { Provider } from 'react-redux'; import HelloWorldContainer from '../components/HelloWorldContainer'; @@ -14,7 +14,7 @@ import HelloWorldContainer from '../components/HelloWorldContainer'; */ export default () => { // This is where we get the existing store. - const store = ReactOnRails.getStore('SharedReduxStore'); + const store = getStore('SharedReduxStore'); return ( diff --git a/spec/dummy/spec/packs_generator_spec.rb b/spec/dummy/spec/packs_generator_spec.rb index 29331470a..bb87ed773 100644 --- a/spec/dummy/spec/packs_generator_spec.rb +++ b/spec/dummy/spec/packs_generator_spec.rb @@ -258,7 +258,7 @@ def self.configuration component_name = "ReactClientComponentWithClientAndServer" component_pack = "#{generated_directory}/#{component_name}.js" pack_content = File.read(component_pack) - expect(pack_content).to include("import ReactOnRails from 'react-on-rails/client';") + expect(pack_content).to include("import * as ReactOnRails from 'react-on-rails/client';") expect(pack_content).to include("ReactOnRails.register({#{component_name}});") expect(pack_content).not_to include("registerServerComponent") end @@ -294,7 +294,7 @@ def self.configuration component_name = "ReactClientComponent" component_pack = "#{generated_directory}/#{component_name}.js" pack_content = File.read(component_pack) - expect(pack_content).to include("import ReactOnRails from 'react-on-rails/client';") + expect(pack_content).to include("import * as ReactOnRails from 'react-on-rails/client';") expect(pack_content).to include("ReactOnRails.register({#{component_name}});") expect(pack_content).not_to include("registerServerComponent") end @@ -310,7 +310,7 @@ def self.configuration component_name = "ReactServerComponent" component_pack = "#{generated_directory}/#{component_name}.js" pack_content = File.read(component_pack) - expect(pack_content).to include("import ReactOnRails from 'react-on-rails/client';") + expect(pack_content).to include("import * as ReactOnRails from 'react-on-rails/client';") expect(pack_content).to include("ReactOnRails.register({#{component_name}});") expect(pack_content).not_to include("registerServerComponent") end @@ -326,7 +326,7 @@ def self.configuration component_name = "ReactServerComponent" component_pack = "#{generated_directory}/#{component_name}.js" pack_content = File.read(component_pack) - expect(pack_content).to include("import ReactOnRails from 'react-on-rails/client';") + expect(pack_content).to include("import * as ReactOnRails from 'react-on-rails/client';") expect(pack_content).to include("ReactOnRails.register({#{component_name}});") expect(pack_content).not_to include("registerServerComponent") end @@ -344,7 +344,7 @@ def self.configuration ) generated_server_bundle_content = File.read(generated_server_bundle_path) expected_content = <<~CONTENT.strip - import ReactOnRails from 'react-on-rails'; + import * as ReactOnRails from 'react-on-rails'; import ReactClientComponent from '../components/ReactServerComponents/ror_components/ReactClientComponent.jsx'; import ReactServerComponent from '../components/ReactServerComponents/ror_components/ReactServerComponent.jsx'; diff --git a/spec/dummy/tests/react-on-rails.import.test.js b/spec/dummy/tests/react-on-rails.import.test.js index c13ef69b8..b81d5cf04 100644 --- a/spec/dummy/tests/react-on-rails.import.test.js +++ b/spec/dummy/tests/react-on-rails.import.test.js @@ -1,4 +1,4 @@ -import ReactOnRails from 'react-on-rails'; +import * as ReactOnRails from 'react-on-rails'; test('ReactOnRails', () => { ReactOnRails.register({}); diff --git a/spec/dummy/tests/react-on-rails.require.test.js b/spec/dummy/tests/react-on-rails.require.test.js index acac258e9..555682922 100644 --- a/spec/dummy/tests/react-on-rails.require.test.js +++ b/spec/dummy/tests/react-on-rails.require.test.js @@ -1,5 +1,5 @@ -const ReactOnRails = require('react-on-rails').default; +const { register } = require('react-on-rails'); test('ReactOnRails', () => { - ReactOnRails.register({}); + register({}); });