plume-ssr-browser
Advanced tools
Comparing version 1.2.0 to 1.3.0
@@ -9,4 +9,3 @@ export { SsrObservableManager } from './lib/observable/SsrObservableManager'; | ||
export { Navigate, useNavigate } from './lib/locationcontext/navigate/Navigate'; | ||
export type { BrowserRenderOption } from './lib/renderer/BrowserApplicationRenderer'; | ||
export { renderBrowserApplication, extractMonitorContextData } from './lib/renderer/BrowserApplicationRenderer'; | ||
export { renderBrowserApplication } from './lib/renderer/BrowserApplicationRenderer'; | ||
export { installRectPlumeSsrFrontendModule } from './lib/module/reactPlumeSsrFrontend-module'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.installRectPlumeSsrFrontendModule = exports.extractMonitorContextData = exports.renderBrowserApplication = exports.useNavigate = exports.Navigate = exports.redirect = exports.SsrLocationContextHolder = exports.useObservable = exports.SsrWritableObservable = exports.SsrObservableManager = void 0; | ||
exports.installRectPlumeSsrFrontendModule = exports.renderBrowserApplication = exports.useNavigate = exports.Navigate = exports.redirect = exports.SsrLocationContextHolder = exports.useObservable = exports.SsrWritableObservable = exports.SsrObservableManager = void 0; | ||
// observable | ||
@@ -20,5 +20,4 @@ var SsrObservableManager_1 = require("./lib/observable/SsrObservableManager"); | ||
Object.defineProperty(exports, "renderBrowserApplication", { enumerable: true, get: function () { return BrowserApplicationRenderer_1.renderBrowserApplication; } }); | ||
Object.defineProperty(exports, "extractMonitorContextData", { enumerable: true, get: function () { return BrowserApplicationRenderer_1.extractMonitorContextData; } }); | ||
var reactPlumeSsrFrontend_module_1 = require("./lib/module/reactPlumeSsrFrontend-module"); | ||
Object.defineProperty(exports, "installRectPlumeSsrFrontendModule", { enumerable: true, get: function () { return reactPlumeSsrFrontend_module_1.installRectPlumeSsrFrontendModule; } }); | ||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsYUFBYTtBQUNiLDhFQUE2RTtBQUFwRSw0SEFBQSxvQkFBb0IsT0FBQTtBQUM3QixnRkFBK0U7QUFBdEUsOEhBQUEscUJBQXFCLE9BQUE7QUFFOUIsZ0VBQStEO0FBQXRELDhHQUFBLGFBQWEsT0FBQTtBQUd0QiwrRUFBb0Y7QUFBM0UsOEhBQUEsd0JBQXdCLE9BQUE7QUFDakMsb0VBQW1FO0FBQTFELG9HQUFBLFFBQVEsT0FBQTtBQUNqQixvRUFBZ0Y7QUFBdkUsb0dBQUEsUUFBUSxPQUFBO0FBQUUsdUdBQUEsV0FBVyxPQUFBO0FBRzlCLHdGQUFnSDtBQUF2RyxzSUFBQSx3QkFBd0IsT0FBQTtBQUFFLHVJQUFBLHlCQUF5QixPQUFBO0FBRTVELDBGQUE4RjtBQUFyRixpSkFBQSxpQ0FBaUMsT0FBQSJ9 | ||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsYUFBYTtBQUNiLDhFQUE2RTtBQUFwRSw0SEFBQSxvQkFBb0IsT0FBQTtBQUM3QixnRkFBK0U7QUFBdEUsOEhBQUEscUJBQXFCLE9BQUE7QUFFOUIsZ0VBQStEO0FBQXRELDhHQUFBLGFBQWEsT0FBQTtBQUd0QiwrRUFBb0Y7QUFBM0UsOEhBQUEsd0JBQXdCLE9BQUE7QUFDakMsb0VBQW1FO0FBQTFELG9HQUFBLFFBQVEsT0FBQTtBQUNqQixvRUFBZ0Y7QUFBdkUsb0dBQUEsUUFBUSxPQUFBO0FBQUUsdUdBQUEsV0FBVyxPQUFBO0FBRTlCLHdGQUFxRjtBQUE1RSxzSUFBQSx3QkFBd0IsT0FBQTtBQUVqQywwRkFBOEY7QUFBckYsaUpBQUEsaUNBQWlDLE9BQUEifQ== |
/// <reference types="react" /> | ||
import { PromiseMonitor } from 'simple-http-rest-client'; | ||
/** | ||
* Options passed to the renderApplication function to configure the application hydration. | ||
* | ||
* Warning: The options used should be exactly the same used to render the SsrApplication, | ||
* otherwise the hydration may fail. | ||
* | ||
* @property {number} maxRender Maximum number of renderings to perform | ||
* before replacing the Html retrieved from the SSR Server with the application | ||
* if the promises are still not resolved. | ||
*/ | ||
export interface BrowserRenderOption { | ||
maxRender: number; | ||
} | ||
/** | ||
* Render the application by hydrating the html received from the SSR Server. | ||
@@ -32,12 +18,3 @@ * | ||
* @param rootElementId Id of the HTML element in which the application will be mounted. | ||
* @param promiseMonitors Array of promise monitors that maintains a state of all the application promises | ||
* that are being executed and that have an impact on the rendering of the application | ||
* @param [option={maxRender:10}] - used to configure the application rendering | ||
*/ | ||
export declare function renderBrowserApplication(reactApp: JSX.Element, rootElementId?: string, promiseMonitors?: PromiseMonitor[], option?: BrowserRenderOption): Promise<void>; | ||
/** | ||
* Extract context data from an array promiseMonitors. | ||
* It is useful especially for logging reasons. | ||
* @param promiseMonitors The promise monitors array | ||
*/ | ||
export declare const extractMonitorContextData: (promiseMonitors: PromiseMonitor[]) => (object | undefined)[]; | ||
export declare function renderBrowserApplication(reactApp: JSX.Element, rootElementId?: string): Promise<void>; |
@@ -26,8 +26,5 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.extractMonitorContextData = exports.renderBrowserApplication = void 0; | ||
exports.renderBrowserApplication = void 0; | ||
const client_1 = require("react-dom/client"); | ||
const ReactDOM = __importStar(require("react-dom/client")); | ||
const simple_logging_system_1 = require("simple-logging-system"); | ||
const HydrationWrapper_1 = require("./HydrationWrapper"); | ||
const logger = new simple_logging_system_1.Logger('RenderBrowserApplication'); | ||
/** | ||
@@ -49,10 +46,4 @@ * Render the application by hydrating the html received from the SSR Server. | ||
* @param rootElementId Id of the HTML element in which the application will be mounted. | ||
* @param promiseMonitors Array of promise monitors that maintains a state of all the application promises | ||
* that are being executed and that have an impact on the rendering of the application | ||
* @param [option={maxRender:10}] - used to configure the application rendering | ||
*/ | ||
async function renderBrowserApplication(reactApp, rootElementId = 'root', promiseMonitors = [], option = { | ||
maxRender: 10, | ||
}) { | ||
const currentMillis = Date.now(); | ||
async function renderBrowserApplication(reactApp, rootElementId = 'root') { | ||
const rootDomElement = document.getElementById(rootElementId); | ||
@@ -63,69 +54,9 @@ if (rootDomElement === null) { | ||
if (rootDomElement.children.length > 0) { | ||
try { | ||
const unMountDummyApp = await renderDummyApp(reactApp); | ||
await prepareApplicationForHydration(promiseMonitors, option); | ||
unMountDummyApp(); | ||
logger.info('All pending promises has been resolved, hydrating the received Html with the app...'); | ||
(0, client_1.hydrateRoot)(rootDomElement, reactApp); | ||
logger.info(`DOM hydrated in ${Date.now() - currentMillis}ms`); | ||
return; | ||
} | ||
catch { | ||
logger.error('DOM hydration failed, rendering app instead'); | ||
} | ||
(0, client_1.hydrateRoot)(rootDomElement, reactApp); | ||
} | ||
ReactDOM.createRoot(rootDomElement).render(reactApp); | ||
logger.info(`DOM rendered in ${Date.now() - currentMillis}ms`); | ||
else { | ||
ReactDOM.createRoot(rootDomElement).render(reactApp); | ||
} | ||
} | ||
exports.renderBrowserApplication = renderBrowserApplication; | ||
function renderDummyApp(reactApp) { | ||
// Try to re-render the application while there are pending Promises | ||
const dummyDiv = document.createElement('div'); | ||
const dummyRoot = ReactDOM.createRoot(dummyDiv); | ||
return new Promise((resolve) => { | ||
const onAppMounted = () => resolve(() => dummyRoot.unmount()); | ||
dummyRoot.render((0, HydrationWrapper_1.hydrationWrapper)(onAppMounted, reactApp)); | ||
}); | ||
} | ||
/** | ||
* Extract context data from an array promiseMonitors. | ||
* It is useful especially for logging reasons. | ||
* @param promiseMonitors The promise monitors array | ||
*/ | ||
const extractMonitorContextData = (promiseMonitors) => promiseMonitors | ||
.flatMap((promiseMonitor) => promiseMonitor | ||
.getRunningPromisesWithInfo() | ||
.map((runningPromise) => runningPromise[1].promiseInfo)); | ||
exports.extractMonitorContextData = extractMonitorContextData; | ||
/** | ||
* Render the application in a dummy Html element until all the data promises are resolved, | ||
* | ||
* @param promiseMonitors | ||
* @param hydrationOption | ||
* @returns Promise resolved if all promises have been resolved, | ||
* Promise rejected if some promises are still pending after the maximum number of returns is reached. | ||
*/ | ||
async function prepareApplicationForHydration(promiseMonitors, hydrationOption) { | ||
const { maxRender } = hydrationOption; | ||
for (let i = 0; i < maxRender; i += 1) { | ||
// attend un cycle supplémentaire pour vérifier que React n'est pas en train de monter des modules dynamiques | ||
// eslint-disable-next-line no-await-in-loop | ||
await new Promise( | ||
// eslint-disable-next-line no-promise-executor-return | ||
(resolve) => setTimeout(resolve, 0)); | ||
if (promiseMonitors.every((promiseMonitor) => promiseMonitor.getRunningPromisesCount() === 0)) { | ||
return Promise.resolve(); | ||
} | ||
logger.info('There are Promises whose resolution is necessary to the hydration, waiting for their resolution...', { | ||
promiseMonitors: (0, exports.extractMonitorContextData)(promiseMonitors), | ||
}); | ||
// eslint-disable-next-line no-await-in-loop | ||
await Promise.allSettled(promiseMonitors.flatMap((promiseMonitor) => promiseMonitor.getRunningPromises())); | ||
logger.info('Re-render the React application now that the Promises have been resolved'); | ||
} | ||
logger.warn(`There are still unresolved promises after ${maxRender} iterations`, { | ||
promiseMonitors: (0, exports.extractMonitorContextData)(promiseMonitors), | ||
}); | ||
return Promise.reject(); | ||
} | ||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQnJvd3NlckFwcGxpY2F0aW9uUmVuZGVyZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3JlbmRlcmVyL0Jyb3dzZXJBcHBsaWNhdGlvblJlbmRlcmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsNkNBQStDO0FBQy9DLDJEQUE2QztBQUU3QyxpRUFBK0M7QUFDL0MseURBQXNEO0FBZ0J0RCxNQUFNLE1BQU0sR0FBRyxJQUFJLDhCQUFNLENBQUMsMEJBQTBCLENBQUMsQ0FBQztBQUV0RDs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUNJLEtBQUssVUFBVSx3QkFBd0IsQ0FDNUMsUUFBcUIsRUFDckIsZ0JBQXdCLE1BQU0sRUFDOUIsa0JBQW9DLEVBQUUsRUFDdEMsU0FBOEI7SUFDNUIsU0FBUyxFQUFFLEVBQUU7Q0FDZDtJQUVELE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUNqQyxNQUFNLGNBQWMsR0FBRyxRQUFRLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQzlELElBQUksY0FBYyxLQUFLLElBQUksRUFBRTtRQUMzQixNQUFNLElBQUksS0FBSyxDQUFDLDRDQUE0QyxhQUFhLEdBQUcsQ0FBQyxDQUFDO0tBQy9FO0lBRUQsSUFBSSxjQUFjLENBQUMsUUFBUSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7UUFDdEMsSUFBSTtZQUNGLE1BQU0sZUFBZSxHQUFHLE1BQU0sY0FBYyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQ3ZELE1BQU0sOEJBQThCLENBQUMsZUFBZSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQzlELGVBQWUsRUFBRSxDQUFDO1lBQ2xCLE1BQU0sQ0FBQyxJQUFJLENBQUMscUZBQXFGLENBQUMsQ0FBQztZQUNuRyxJQUFBLG9CQUFXLEVBQUMsY0FBYyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQ3RD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 | ||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQnJvd3NlckFwcGxpY2F0aW9uUmVuZGVyZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3JlbmRlcmVyL0Jyb3dzZXJBcHBsaWNhdGlvblJlbmRlcmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsNkNBQStDO0FBQy9DLDJEQUE2QztBQUU3Qzs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNJLEtBQUssVUFBVSx3QkFBd0IsQ0FDNUMsUUFBcUIsRUFDckIsZ0JBQXdCLE1BQU07SUFFOUIsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUM5RCxJQUFJLGNBQWMsS0FBSyxJQUFJLEVBQUU7UUFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw0Q0FBNEMsYUFBYSxHQUFHLENBQUMsQ0FBQztLQUMvRTtJQUVELElBQUksY0FBYyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO1FBQ3RDLElBQUEsb0JBQVcsRUFBQyxjQUFjLEVBQUUsUUFBUSxDQUFDLENBQUM7S0FDdkM7U0FBTTtRQUNMLFFBQVEsQ0FBQyxVQUFVLENBQUMsY0FBYyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0tBQ3REO0FBQ0gsQ0FBQztBQWRELDREQWNDIn0= |
@@ -9,4 +9,3 @@ export { SsrObservableManager } from './lib/observable/SsrObservableManager'; | ||
export { Navigate, useNavigate } from './lib/locationcontext/navigate/Navigate'; | ||
export type { BrowserRenderOption } from './lib/renderer/BrowserApplicationRenderer'; | ||
export { renderBrowserApplication, extractMonitorContextData } from './lib/renderer/BrowserApplicationRenderer'; | ||
export { renderBrowserApplication } from './lib/renderer/BrowserApplicationRenderer'; | ||
export { installRectPlumeSsrFrontendModule } from './lib/module/reactPlumeSsrFrontend-module'; |
@@ -8,4 +8,4 @@ // observable | ||
export { Navigate, useNavigate } from './lib/locationcontext/navigate/Navigate'; | ||
export { renderBrowserApplication, extractMonitorContextData } from './lib/renderer/BrowserApplicationRenderer'; | ||
export { renderBrowserApplication } from './lib/renderer/BrowserApplicationRenderer'; | ||
export { installRectPlumeSsrFrontendModule } from './lib/module/reactPlumeSsrFrontend-module'; | ||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsYUFBYTtBQUNiLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQzdFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHdDQUF3QyxDQUFDO0FBRS9FLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUcvRCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUNwRixPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDbkUsT0FBTyxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUdoRixPQUFPLEVBQUUsd0JBQXdCLEVBQUUseUJBQXlCLEVBQUUsTUFBTSwyQ0FBMkMsQ0FBQztBQUVoSCxPQUFPLEVBQUUsaUNBQWlDLEVBQUUsTUFBTSwyQ0FBMkMsQ0FBQyJ9 | ||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsYUFBYTtBQUNiLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQzdFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHdDQUF3QyxDQUFDO0FBRS9FLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUcvRCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUNwRixPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDbkUsT0FBTyxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUVoRixPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSwyQ0FBMkMsQ0FBQztBQUVyRixPQUFPLEVBQUUsaUNBQWlDLEVBQUUsTUFBTSwyQ0FBMkMsQ0FBQyJ9 |
/// <reference types="react" /> | ||
import { PromiseMonitor } from 'simple-http-rest-client'; | ||
/** | ||
* Options passed to the renderApplication function to configure the application hydration. | ||
* | ||
* Warning: The options used should be exactly the same used to render the SsrApplication, | ||
* otherwise the hydration may fail. | ||
* | ||
* @property {number} maxRender Maximum number of renderings to perform | ||
* before replacing the Html retrieved from the SSR Server with the application | ||
* if the promises are still not resolved. | ||
*/ | ||
export interface BrowserRenderOption { | ||
maxRender: number; | ||
} | ||
/** | ||
* Render the application by hydrating the html received from the SSR Server. | ||
@@ -32,12 +18,3 @@ * | ||
* @param rootElementId Id of the HTML element in which the application will be mounted. | ||
* @param promiseMonitors Array of promise monitors that maintains a state of all the application promises | ||
* that are being executed and that have an impact on the rendering of the application | ||
* @param [option={maxRender:10}] - used to configure the application rendering | ||
*/ | ||
export declare function renderBrowserApplication(reactApp: JSX.Element, rootElementId?: string, promiseMonitors?: PromiseMonitor[], option?: BrowserRenderOption): Promise<void>; | ||
/** | ||
* Extract context data from an array promiseMonitors. | ||
* It is useful especially for logging reasons. | ||
* @param promiseMonitors The promise monitors array | ||
*/ | ||
export declare const extractMonitorContextData: (promiseMonitors: PromiseMonitor[]) => (object | undefined)[]; | ||
export declare function renderBrowserApplication(reactApp: JSX.Element, rootElementId?: string): Promise<void>; |
import { hydrateRoot } from 'react-dom/client'; | ||
import * as ReactDOM from 'react-dom/client'; | ||
import { Logger } from 'simple-logging-system'; | ||
import { hydrationWrapper } from './HydrationWrapper'; | ||
const logger = new Logger('RenderBrowserApplication'); | ||
/** | ||
@@ -22,10 +19,4 @@ * Render the application by hydrating the html received from the SSR Server. | ||
* @param rootElementId Id of the HTML element in which the application will be mounted. | ||
* @param promiseMonitors Array of promise monitors that maintains a state of all the application promises | ||
* that are being executed and that have an impact on the rendering of the application | ||
* @param [option={maxRender:10}] - used to configure the application rendering | ||
*/ | ||
export async function renderBrowserApplication(reactApp, rootElementId = 'root', promiseMonitors = [], option = { | ||
maxRender: 10, | ||
}) { | ||
const currentMillis = Date.now(); | ||
export async function renderBrowserApplication(reactApp, rootElementId = 'root') { | ||
const rootDomElement = document.getElementById(rootElementId); | ||
@@ -36,67 +27,8 @@ if (rootDomElement === null) { | ||
if (rootDomElement.children.length > 0) { | ||
try { | ||
const unMountDummyApp = await renderDummyApp(reactApp); | ||
await prepareApplicationForHydration(promiseMonitors, option); | ||
unMountDummyApp(); | ||
logger.info('All pending promises has been resolved, hydrating the received Html with the app...'); | ||
hydrateRoot(rootDomElement, reactApp); | ||
logger.info(`DOM hydrated in ${Date.now() - currentMillis}ms`); | ||
return; | ||
} | ||
catch { | ||
logger.error('DOM hydration failed, rendering app instead'); | ||
} | ||
hydrateRoot(rootDomElement, reactApp); | ||
} | ||
ReactDOM.createRoot(rootDomElement).render(reactApp); | ||
logger.info(`DOM rendered in ${Date.now() - currentMillis}ms`); | ||
} | ||
function renderDummyApp(reactApp) { | ||
// Try to re-render the application while there are pending Promises | ||
const dummyDiv = document.createElement('div'); | ||
const dummyRoot = ReactDOM.createRoot(dummyDiv); | ||
return new Promise((resolve) => { | ||
const onAppMounted = () => resolve(() => dummyRoot.unmount()); | ||
dummyRoot.render(hydrationWrapper(onAppMounted, reactApp)); | ||
}); | ||
} | ||
/** | ||
* Extract context data from an array promiseMonitors. | ||
* It is useful especially for logging reasons. | ||
* @param promiseMonitors The promise monitors array | ||
*/ | ||
export const extractMonitorContextData = (promiseMonitors) => promiseMonitors | ||
.flatMap((promiseMonitor) => promiseMonitor | ||
.getRunningPromisesWithInfo() | ||
.map((runningPromise) => runningPromise[1].promiseInfo)); | ||
/** | ||
* Render the application in a dummy Html element until all the data promises are resolved, | ||
* | ||
* @param promiseMonitors | ||
* @param hydrationOption | ||
* @returns Promise resolved if all promises have been resolved, | ||
* Promise rejected if some promises are still pending after the maximum number of returns is reached. | ||
*/ | ||
async function prepareApplicationForHydration(promiseMonitors, hydrationOption) { | ||
const { maxRender } = hydrationOption; | ||
for (let i = 0; i < maxRender; i += 1) { | ||
// attend un cycle supplémentaire pour vérifier que React n'est pas en train de monter des modules dynamiques | ||
// eslint-disable-next-line no-await-in-loop | ||
await new Promise( | ||
// eslint-disable-next-line no-promise-executor-return | ||
(resolve) => setTimeout(resolve, 0)); | ||
if (promiseMonitors.every((promiseMonitor) => promiseMonitor.getRunningPromisesCount() === 0)) { | ||
return Promise.resolve(); | ||
} | ||
logger.info('There are Promises whose resolution is necessary to the hydration, waiting for their resolution...', { | ||
promiseMonitors: extractMonitorContextData(promiseMonitors), | ||
}); | ||
// eslint-disable-next-line no-await-in-loop | ||
await Promise.allSettled(promiseMonitors.flatMap((promiseMonitor) => promiseMonitor.getRunningPromises())); | ||
logger.info('Re-render the React application now that the Promises have been resolved'); | ||
else { | ||
ReactDOM.createRoot(rootDomElement).render(reactApp); | ||
} | ||
logger.warn(`There are still unresolved promises after ${maxRender} iterations`, { | ||
promiseMonitors: extractMonitorContextData(promiseMonitors), | ||
}); | ||
return Promise.reject(); | ||
} | ||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQnJvd3NlckFwcGxpY2F0aW9uUmVuZGVyZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3JlbmRlcmVyL0Jyb3dzZXJBcHBsaWNhdGlvblJlbmRlcmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUMvQyxPQUFPLEtBQUssUUFBUSxNQUFNLGtCQUFrQixDQUFDO0FBRTdDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQWdCdEQsTUFBTSxNQUFNLEdBQUcsSUFBSSxNQUFNLENBQUMsMEJBQTBCLENBQUMsQ0FBQztBQUV0RDs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsd0JBQXdCLENBQzVDLFFBQXFCLEVBQ3JCLGdCQUF3QixNQUFNLEVBQzlCLGtCQUFvQyxFQUFFLEVBQ3RDLFNBQThCO0lBQzVCLFNBQVMsRUFBRSxFQUFFO0NBQ2Q7SUFFRCxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDakMsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUM5RCxJQUFJLGNBQWMsS0FBSyxJQUFJLEVBQUU7UUFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw0Q0FBNEMsYUFBYSxHQUFHLENBQUMsQ0FBQztLQUMvRTtJQUVELElBQUksY0FBYyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO1FBQ3RDLElBQUk7WUFDRixNQUFNLGVBQWUsR0FBRyxNQUFNLGNBQWMsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUN2RCxNQUFNLDhCQUE4QixDQUFDLGVBQWUsRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM5RCxlQUFlLEVBQUUs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 | ||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQnJvd3NlckFwcGxpY2F0aW9uUmVuZGVyZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3JlbmRlcmVyL0Jyb3dzZXJBcHBsaWNhdGlvblJlbmRlcmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUMvQyxPQUFPLEtBQUssUUFBUSxNQUFNLGtCQUFrQixDQUFDO0FBRTdDOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSx3QkFBd0IsQ0FDNUMsUUFBcUIsRUFDckIsZ0JBQXdCLE1BQU07SUFFOUIsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUM5RCxJQUFJLGNBQWMsS0FBSyxJQUFJLEVBQUU7UUFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw0Q0FBNEMsYUFBYSxHQUFHLENBQUMsQ0FBQztLQUMvRTtJQUVELElBQUksY0FBYyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO1FBQ3RDLFdBQVcsQ0FBQyxjQUFjLEVBQUUsUUFBUSxDQUFDLENBQUM7S0FDdkM7U0FBTTtRQUNMLFFBQVEsQ0FBQyxVQUFVLENBQUMsY0FBYyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0tBQ3REO0FBQ0gsQ0FBQyJ9 |
{ | ||
"name": "plume-ssr-browser", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "A framework to create an SSR server for a React Plume project", | ||
@@ -5,0 +5,0 @@ "author": "Aurélien Manteaux <amanteaux@coreoz.com> (https://coreoz.com)", |
Plume SSR - Browser | ||
=================== | ||
The frontend part of [Plume SSR](https://github.com/Coreoz/plume-ssr). |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
0
110800
52
731