UNPKG

ilc-adapter-react

Version:
136 lines (135 loc) 7.35 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.IlcAdapterReact = exports.SingleSpaContext = void 0; exports.default = ilcAdapterReact; const react_1 = __importDefault(require("react")); const client_1 = require("react-dom/client"); const errors_1 = require("./errors"); const AdapterErrorBoundary_1 = __importDefault(require("./AdapterErrorBoundary")); // React context that gives any react component the single-spa props exports.SingleSpaContext = react_1.default.createContext({}); class IlcAdapterReact { constructor(userOpts) { this.domElements = {}; this.reactRoots = {}; this.bootstrap = (props) => __awaiter(this, void 0, void 0, function* () { if ('rootComponent' in this.userOpts) { // This is a class or stateless function component this.rootComponent = this.userOpts.rootComponent; } else if (this.userOpts.loadRootComponent) { // They passed a promise that resolves with the react component. Wait for it to resolve before mounting this.rootComponent = yield this.userOpts.loadRootComponent(props); } else if (!this.rootComponent) { throw new Error(`ilc-adapter-react: must be passed opts.rootComponent or opts.loadRootComponent`); } }); this.mount = (props) => __awaiter(this, void 0, void 0, function* () { if (!this.rootComponent) { throw new errors_1.IlcAdapterError(`ilc-adapter-react: Looks like "mount" was called before completion of the "bootstrap"`); } const domElement = this.chooseDomElementGetter(props)(); if (!domElement) { throw new errors_1.IlcAdapterError(`ilc-adapter-react: domElementGetter function for application '${props.name}' did not return a valid dom element. Please pass a valid domElement or domElementGetter via opts or props`); } const elementToRender = this.getElementToRender(this.rootComponent, props); let reactRoot = this.reactRoots[props.name]; reactRoot = this.reactDomRender(elementToRender, domElement, !!reactRoot, reactRoot, props); this.reactRoots[props.name] = reactRoot; this.domElements[props.name] = domElement; return reactRoot; }); this.unmount = (props) => __awaiter(this, void 0, void 0, function* () { const reactRoot = this.reactRoots[props.name]; if (reactRoot) { reactRoot.unmount(); delete this.reactRoots[props.name]; delete this.domElements[props.name]; } }); this.update = (props) => __awaiter(this, void 0, void 0, function* () { if (!this.rootComponent) { throw new errors_1.IlcAdapterError(`ilc-adapter-react: Looks like "update" was called before completion of the "bootstrap"`); } const domElement = this.domElements[props.name]; if (domElement === undefined) { throw new errors_1.IlcAdapterError(`ilc-adapter-react: Looks like "update" was called before "mount" or after "unmount"`); } const elementToRender = this.getElementToRender(this.rootComponent, props); const reactRoot = this.reactRoots[props.name]; if (reactRoot === undefined) { throw new errors_1.IlcAdapterError(`ilc-adapter-react: Looks like "update" was called before "mount" or after "unmount". Root node is not defined`); } return this.reactDomRender(elementToRender, domElement, true, reactRoot, props); }); if (typeof userOpts !== 'object') { throw new Error(`ilc-adapter-react requires a configuration object`); } this.userOpts = Object.assign(Object.assign({}, userOpts), { parcelCanUpdate: true }); if (this.userOpts.parcelCanUpdate === false) { this.update = undefined; } } chooseDomElementGetter(props) { if ('domElement' in props) { return () => props.domElement; } else if (props.domElementGetter) { if (typeof props.domElementGetter !== 'function') { throw new Error(`ilc-adapter-react: the domElementGetter for react application '${props.name}' is not a function`); } return props.domElementGetter; } throw new errors_1.IlcAdapterError(`ilc-adapter-react: Unable to identify DOM node to app/parcel mount`); } getElementToRender(component, props) { const rootComponentElement = react_1.default.createElement(component, props); const errorBoundary = this.userOpts.errorBoundary ? (caughtError, caughtErrorInfo) => this.userOpts.errorBoundary(caughtError, caughtErrorInfo, props) : undefined; const errorHandler = props.errorHandler ? props.errorHandler : (error) => { console.error(`ilc-adapter-react: app or parcel "${props.name}" have thrown an uncaught error:`, error); }; return (react_1.default.createElement(exports.SingleSpaContext.Provider, { value: props }, react_1.default.createElement(AdapterErrorBoundary_1.default, { onError: errorHandler, errorBoundary: errorBoundary }, rootComponentElement))); } reactDomRender(elementToRender, domElement, forceRender = false, reactRoot, props) { if (!forceRender && domElement.childElementCount > 0) { //We're likely rendering app after SSR const { onRecoverableError } = this.userOpts; const onRecoverableErrorWrapped = onRecoverableError ? (error, errorInfo) => onRecoverableError(error, errorInfo, props) : undefined; return (0, client_1.hydrateRoot)(domElement, elementToRender, { onRecoverableError: onRecoverableErrorWrapped }); } // default to this if 'renderType' is null or doesn't match the other options if (!reactRoot) { const root = (0, client_1.createRoot)(domElement); root.render(elementToRender); return root; } else { reactRoot.render(elementToRender); return reactRoot; } } } exports.IlcAdapterReact = IlcAdapterReact; function ilcAdapterReact(userOpts) { return new IlcAdapterReact(userOpts); }