/** * takes a SlsIsomorphic Component and parses it into an Iso-Config * * @param component a SlsIsomorphic React-Component */ import {ConfigTypes} from "./lib/config"; import React from 'react'; import * as deepmerge from 'deepmerge'; const isClientApp = (component) => { return component.props && component.props.id !== undefined && component.props.path !== undefined && component.props.method !== undefined ? true : false; }; const isMiddleware = (component) => { return component.props && component.props.callback !== undefined ? true : false; }; const isRedirect = (component) => { return component.props && component.props.from !== undefined && component.props.to !== undefined && component.props.status !== undefined ? true : false; }; const isRoute = (component) => { return component.props && component.props.path !== undefined && (component.props.render !== undefined || component.props.component !== undefined) && component.props.name !== undefined ? true : false; }; export const parseCustomComponent = (component, compileMode) => { try { //console.log("parseCustomComponent: " , component); const params = Object.assign({ infrastructureMode: compileMode ? "compile" : undefined, }, component.props); var custom = undefined; const parsed = `const f=${component.type}; f(${JSON.stringify(params)})`; const result = eval(parsed); //console.log("isCustomComponent: ", component) //console.log("parsed: ", parsed); //console.log("result: ", result); return result.infrastructureType !== undefined ? result : undefined; } catch (error) { //console.error(error); return undefined; } } export const getChildrenArray = (component) => { if (component == undefined) { return []; } if (Array.isArray(component) && component.length > 0) { //console.log("component is array: ", component) return [...component] ; } if (component.props == undefined || component.props.children == undefined) { return []; } return Array.isArray(component.props.children) ? component.props.children : [component.props.children]; }; const applyMiddleware = (mwComponent) => { return mwComponent.props.callback; }; const parseMiddlewares = (component) => { return getChildrenArray(component) .filter(child => isMiddleware(child)) .map(child => applyMiddleware(child)); } const applyClientApp = (caComponent) => { //console.log("applyClientApp: " , caComponent); return Object.assign( Object.assign({}, caComponent.props), { middlewareCallbacks: (caComponent.props.middlewareCallbacks !== undefined ? caComponent.props.middlewareCallbacks : []).concat(parseMiddlewares(caComponent)), redirects: (caComponent.props.redirects !== undefined ? caComponent.props.redirects : []).concat(parseRedirects(caComponent)), routes: (caComponent.props.routes !== undefined ? caComponent.props.routes : []).concat(parseRoutes(caComponent, caComponent.props.method)), } ); }; export const applyCustomComponents = (component: any, addToTopLevelConfig, addDataLayer, compileMode) => { //getChildrenArray(caComponent).forEach( c => { const customComponent = parseCustomComponent(component, compileMode); if (customComponent !== undefined && compileMode) { //console.log("CustomComponent: ", customComponent); if (customComponent.infrastructureType === "dataLayer") { addDataLayer(customComponent); } // now add to the configuration addToTopLevelConfig(customComponent); // we expect a single one child!! if (Array.isArray(customComponent.children)) { throw new Error("custom Components must have a single one child!"); } //console.log("component: " , component); //return component.props.children var customProps = {} customProps[customComponent.infrastructureType] = Object.assign( {}, customComponent /*component.props*/, {infrastructureMode: "component"} ) const child = component.props.children; // add the custom props to the child that is forwarded return child !== undefined ? React.cloneElement(child, Object.assign({}, child, customProps)) : () => {} } else if (customComponent !== undefined) { //console.log("applyCustomComponents | customComponent ") if (React.isValidElement(component)) { //console.log("custom component is a react-component, " , component) if (Array.isArray(customComponent.children)) { throw new Error("custom Components must have a single one child!"); } const child = component["props"]["children"]; var customProps = {} customProps[customComponent.infrastructureType] = React.cloneElement(component, Object.assign({}, component.props, {infrastructureMode: "component"})) //console.log("customProps: " , customProps); const result = React.cloneElement(component, Object.assign({}, child !== undefined ? child.props : {}, customProps)); if (customComponent.infrastructureType === "dataLayer") { addDataLayer(result); } return result; //return React.cloneElement(component, Object.assign({}, component.props, {infrastructureMode: "component"})) } return component.props.children; } // when the component is NOT a custom one, we return it return component; //}); } const parseRedirects = (component) => { return getChildrenArray(component) .filter(child => isRedirect(child)) .map(child => applyRedirect(child)); }; const applyRedirect = (redirectComponent) => { //console.log("redirect: ", redirectComponent.props); return redirectComponent.props }; const parseRoutes = (component, method) => { /*return getChildrenArray(component) .filter(child => isRoute(child)) .map(child => applyRoute(child, component.props.method)); */ return getChildrenArray(component) .map(child => { //console.log("child: ", child) if (isRoute(child)) { return applyRoute(child, method); } else if (!isMiddleware(child) && !isRedirect(child)) { //console.log("investigate child: ", child) return parseRoutes(child, method) } else return []; }) .flat(); }; const applyRoute = (routeComponent, method) => { //console.log("route: ", routeComponent.props); return Object.assign( Object.assign({}, routeComponent.props), { method: method, exact: true, middlewareCallbacks: (routeComponent.props.middlewareCallbacks !== undefined ? routeComponent.props.middlewareCallbacks : []).concat(parseMiddlewares(routeComponent)), } ); }; export function loadIsoConfigFromComponent(component: any, compileMode: boolean = true) { //console.log("child: ", component.props.children.props); var arrConfigs = []; const addToTopLevelConfig = (c) => { //console.log("addToTopLevelConfig: ", c); const allowed = ['slsConfig', 'ssrConfig']; arrConfigs.push(Object.keys(c) .filter(key => allowed.includes(key)) .reduce((obj, key) => { obj[key] = c[key]; return obj; }, {}) ); } var arrDataLayers = []; const addDataLayer = (dlComponent) => { arrDataLayers.push(dlComponent); } const clientApps= getChildrenArray(component) .map(child => applyCustomComponents(child, addToTopLevelConfig, addDataLayer, compileMode)) .filter(child => isClientApp(child)) .map(child => applyClientApp(child)); //console.log("arrConfigs: " , arrConfigs) const result = deepmerge.all([{ type: ConfigTypes.ISOMORPHIC, isoConfig: { middlewares: parseMiddlewares(component), clientApps: clientApps, dataLayers: arrDataLayers }, ssrConfig: { stackName: component.props.stackName, buildPath: component.props.buildPath, assetsPath: component.props.assetsPath, region: component.props.region }, slsConfig: {} }, ...arrConfigs ]); //console.log("loaded IsoConfig: " , result); return result; }