Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions node_package/src/Authenticity.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import type { AuthenticityHeaders } from './types/index';

export default {
authenticityToken(): string | null {
export function authenticityToken(): string | null {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const token = document.querySelector('meta[name="csrf-token"]');
if (token instanceof HTMLMetaElement) {
return token.content;
}
return null;
},
}

authenticityHeaders(otherHeaders: Record<string, string> = {}): AuthenticityHeaders {
return Object.assign(otherHeaders, {
'X-CSRF-Token': this.authenticityToken(),
export const authenticityHeaders = (otherHeaders: Record<string, string> = {}): AuthenticityHeaders =>
Object.assign(otherHeaders, {
'X-CSRF-Token': authenticityToken(),
'X-Requested-With': 'XMLHttpRequest',
});
},
};
24 changes: 9 additions & 15 deletions node_package/src/ComponentRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import CallbackRegistry from './CallbackRegistry';

const componentRegistry = new CallbackRegistry<RegisteredComponent>('component');

export default {
/**
* @param components { component1: component1, component2: component2, etc. }
*/
register(components: Record<string, ReactComponentOrRenderFunction>): void {
export function register(components: Record<string, ReactComponentOrRenderFunction>): void {
Object.keys(components).forEach((name) => {
if (componentRegistry.has(name)) {
console.warn('Called register for component that is already registered', name);
Expand All @@ -29,30 +28,25 @@ export default {
isRenderer,
});
});
},
}

/**
* @param name
* @returns { name, component, isRenderFunction, isRenderer }
*/
get(name: string): RegisteredComponent {
return componentRegistry.get(name);
},
export const get = (name: string): RegisteredComponent => componentRegistry.get(name);

getOrWaitForComponent(name: string): Promise<RegisteredComponent> {
return componentRegistry.getOrWaitForItem(name);
},
export const getOrWaitForComponent = (name: string): Promise<RegisteredComponent> =>
componentRegistry.getOrWaitForItem(name);

/**
* Get a Map containing all registered components. Useful for debugging.
* @returns Map where key is the component name and values are the
* { name, component, renderFunction, isRenderer}
*/
components(): Map<string, RegisteredComponent> {
return componentRegistry.getAll();
},
export const components = (): Map<string, RegisteredComponent> => componentRegistry.getAll();

clear(): void {
/** @internal Exported only for tests */
export function clear(): void {
componentRegistry.clear();
},
};
}
6 changes: 3 additions & 3 deletions node_package/src/ReactOnRails.client.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { ReactElement } from 'react';
import * as ClientStartup from './clientStartup';
import { renderOrHydrateComponent, hydrateStore } from './ClientSideRenderer';
import ComponentRegistry from './ComponentRegistry';
import StoreRegistry from './StoreRegistry';
import * as ComponentRegistry from './ComponentRegistry';
import * as StoreRegistry from './StoreRegistry';
import buildConsoleReplay from './buildConsoleReplay';
import createReactOutput from './createReactOutput';
import Authenticity from './Authenticity';
import * as Authenticity from './Authenticity';
import context from './context';
import type {
RegisteredComponent,
Expand Down
10 changes: 5 additions & 5 deletions node_package/src/RenderUtils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
export default {
wrapInScriptTags(scriptId: string, scriptBody: string): string {
// eslint-disable-next-line import/prefer-default-export -- only one export for now, but others may be added later
export function wrapInScriptTags(scriptId: string, scriptBody: string): string {
if (!scriptBody) {
return '';
}

return `\n<script id="${scriptId}">
return `
<script id="${scriptId}">
${scriptBody}
</script>`;
},
};
}
48 changes: 19 additions & 29 deletions node_package/src/StoreRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,27 @@ import type { Store, StoreGenerator } from './types';
const storeGeneratorRegistry = new CallbackRegistry<StoreGenerator>('store generator');
const hydratedStoreRegistry = new CallbackRegistry<Store>('hydrated store');

export default {
/**
* Register a store generator, a function that takes props and returns a store.
* @param storeGenerators { name1: storeGenerator1, name2: storeGenerator2 }
*/
register(storeGenerators: Record<string, StoreGenerator>): void {
export function register(storeGenerators: Record<string, StoreGenerator>): void {
Object.keys(storeGenerators).forEach((name) => {
if (storeGeneratorRegistry.has(name)) {
console.warn('Called registerStore for store that is already registered', name);
}

const store = storeGenerators[name];
if (!store) {
const storeGenerator = storeGenerators[name];
if (!storeGenerator) {
throw new Error(
'Called ReactOnRails.registerStores with a null or undefined as a value ' +
'Called ReactOnRails.registerStoreGenerators with a null or undefined as a value ' +
`for the store generator with key ${name}.`,
);
}

storeGeneratorRegistry.set(name, store);
storeGeneratorRegistry.set(name, storeGenerator);
});
},
}

/**
* Used by components to get the hydrated store which contains props.
Expand All @@ -34,7 +33,7 @@ export default {
* there is no store with the given name.
* @returns Redux Store, possibly hydrated
*/
getStore(name: string, throwIfMissing = true): Store | undefined {
export function getStore(name: string, throwIfMissing = true): Store | undefined {
try {
return hydratedStoreRegistry.get(name);
} catch (error) {
Expand All @@ -52,64 +51,55 @@ This can happen if you are server rendering and either:
}
return undefined;
}
},
}

/**
* Internally used function to get the store creator that was passed to `register`.
* @param name
* @returns storeCreator with given name
*/
getStoreGenerator(name: string): StoreGenerator {
return storeGeneratorRegistry.get(name);
},
export const getStoreGenerator = (name: string): StoreGenerator => storeGeneratorRegistry.get(name);

/**
* Internally used function to set the hydrated store after a Rails page is loaded.
* @param name
* @param store (not the storeGenerator, but the hydrated store)
*/
setStore(name: string, store: Store): void {
export function setStore(name: string, store: Store): void {
hydratedStoreRegistry.set(name, store);
},
}

/**
* Internally used function to completely clear hydratedStores Map.
*/
clearHydratedStores(): void {
export function clearHydratedStores(): void {
hydratedStoreRegistry.clear();
},
}

/**
* Get a Map containing all registered store generators. Useful for debugging.
* @returns Map where key is the component name and values are the store generators.
*/
storeGenerators(): Map<string, StoreGenerator> {
return storeGeneratorRegistry.getAll();
},
export const storeGenerators = (): Map<string, StoreGenerator> => storeGeneratorRegistry.getAll();

/**
* Get a Map containing all hydrated stores. Useful for debugging.
* @returns Map where key is the component name and values are the hydrated stores.
*/
stores(): Map<string, Store> {
return hydratedStoreRegistry.getAll();
},
export const stores = (): Map<string, Store> => hydratedStoreRegistry.getAll();

/**
* Used by components to get the hydrated store, waiting for it to be hydrated if necessary.
* @param name Name of the store to wait for
* @returns Promise that resolves with the Store once hydrated
*/
getOrWaitForStore(name: string): Promise<Store> {
return hydratedStoreRegistry.getOrWaitForItem(name);
},
export const getOrWaitForStore = (name: string): Promise<Store> =>
hydratedStoreRegistry.getOrWaitForItem(name);

/**
* Used by components to get the store generator, waiting for it to be registered if necessary.
* @param name Name of the store generator to wait for
* @returns Promise that resolves with the StoreGenerator once registered
*/
getOrWaitForStoreGenerator(name: string): Promise<StoreGenerator> {
return storeGeneratorRegistry.getOrWaitForItem(name);
},
};
export const getOrWaitForStoreGenerator = (name: string): Promise<StoreGenerator> =>
storeGeneratorRegistry.getOrWaitForItem(name);
7 changes: 2 additions & 5 deletions node_package/src/buildConsoleReplay.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import RenderUtils from './RenderUtils';
import { wrapInScriptTags } from './RenderUtils';
import scriptSanitizedVal from './scriptSanitizedVal';

declare global {
Expand Down Expand Up @@ -54,8 +54,5 @@ export default function buildConsoleReplay(
customConsoleHistory: (typeof console)['history'] | undefined = undefined,
numberOfMessagesToSkip: number = 0,
): string {
return RenderUtils.wrapInScriptTags(
'consoleReplayLog',
consoleReplay(customConsoleHistory, numberOfMessagesToSkip),
);
return wrapInScriptTags('consoleReplayLog', consoleReplay(customConsoleHistory, numberOfMessagesToSkip));
}
7 changes: 2 additions & 5 deletions node_package/src/serverRenderReactComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';
import type { ReactElement } from 'react';

import ComponentRegistry from './ComponentRegistry';
import * as ComponentRegistry from './ComponentRegistry';
import createReactOutput from './createReactOutput';
import { isPromise, isServerRenderHash } from './isServerRenderResult';
import buildConsoleReplay from './buildConsoleReplay';
Expand Down Expand Up @@ -144,10 +144,7 @@ function serverRenderReactComponentInternal(options: RenderParams): null | strin
throwJsErrors,
} = options;

let renderState: RenderState = {
result: null,
hasErrors: false,
};
let renderState: RenderState;

try {
const componentObj = ComponentRegistry.get(componentName);
Expand Down
2 changes: 1 addition & 1 deletion node_package/src/streamServerRenderedReactComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';
import { PassThrough, Readable } from 'stream';

import ComponentRegistry from './ComponentRegistry';
import * as ComponentRegistry from './ComponentRegistry';
import createReactOutput from './createReactOutput';
import { isPromise, isServerRenderHash } from './isServerRenderResult';
import buildConsoleReplay from './buildConsoleReplay';
Expand Down
2 changes: 1 addition & 1 deletion node_package/tests/ComponentRegistry.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import * as React from 'react';
import * as createReactClass from 'create-react-class';

import ComponentRegistry from '../src/ComponentRegistry';
import * as ComponentRegistry from '../src/ComponentRegistry';

const onPageLoadedCallbacks = [];
const onPageUnloadedCallbacks = [];
Expand Down
6 changes: 3 additions & 3 deletions node_package/tests/StoreRegistry.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createStore } from 'redux';

import StoreRegistry from '../src/StoreRegistry';
import * as StoreRegistry from '../src/StoreRegistry';

function reducer() {
return {};
Expand All @@ -20,10 +20,10 @@ describe('', () => {
expect.assertions(2);
StoreRegistry.stores().clear();
expect(() => StoreRegistry.register({ storeGenerator: null })).toThrow(
/Called ReactOnRails.registerStores with a null or undefined as a value/,
/Called ReactOnRails.registerStoreGenerators with a null or undefined as a value/,
);
expect(() => StoreRegistry.register({ storeGenerator: undefined })).toThrow(
/Called ReactOnRails.registerStores with a null or undefined as a value/,
/Called ReactOnRails.registerStoreGenerators with a null or undefined as a value/,
);
});

Expand Down
2 changes: 1 addition & 1 deletion node_package/tests/serverRenderReactComponent.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import serverRenderReactComponent from '../src/serverRenderReactComponent';
import ComponentRegistry from '../src/ComponentRegistry';
import * as ComponentRegistry from '../src/ComponentRegistry';
import type {
RenderParams,
RenderResult,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import streamServerRenderedReactComponent from '../src/streamServerRenderedReactComponent';
import ComponentRegistry from '../src/ComponentRegistry';
import * as ComponentRegistry from '../src/ComponentRegistry';

const AsyncContent = async ({ throwAsyncError }) => {
await new Promise((resolve) => {
Expand Down
Loading