Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
/**
 * @module responsible for generating the HTML page response
 * @see the react application middleware
 */
/* eslint-disable react/no-danger */
/* eslint-disable react/no-array-index-key */
import React from 'react';
import { oneRouter } from '@skava/router';
import { isArray, isObj } from 'exotic';
import serialize from 'serialize-javascript';
import { getClientBundleEntryAssets } from '../../aliased';
import { logger } from '../../log';
import { HTML } from '../Document';
import { findAssetsForRoute } from './findAssetsForRoute';
import { oneStorage } from '@skava/persistence';
import { fromClientPathToCompatPath, } from './compat';
const clientEntryAssets = getClientBundleEntryAssets();
// --------------- config ---------------
const HAS_STYLES = false;
// ----------------- components ------------------
/**
 * make sure we don't give any `map` files
 * @todo we could filter this elsewhere
 * @param path @example /eh.js
 */
function shouldRemoveFromServerJavaScriptList(path) {
    return path.includes('.map') === false;
}
/**
 * @throws Invariant Violation: React.Children.only expected to receive a single React element child.
 * return React.Children.only(props.children)
 */
function KeyedComponent(props) {
    return props.children;
}
const toKeyed = (x, index) => (React.createElement(KeyedComponent, { key: index }, x));
function stylesheetTag(stylesheetFilePath) {
    return React.createElement("link", { href: stylesheetFilePath, media: "screen, projection", rel: "stylesheet" });
}
function hackFilePath(jsFilePath) {
    const pathWithSlash = jsFilePath.includes('/') ? jsFilePath : '/' + jsFilePath;
    const hackedPath = pathWithSlash.replace('undefined', '');
    const absPath = hackedPath.startsWith('/') ? oneRouter.origin + hackedPath : hackedPath;
    const hackedAbsPath = absPath.replace('undefined/', '');
    const finalPath = hackedAbsPath.startsWith('/') || hackedAbsPath.includes('http')
        ? hackedAbsPath
        : '/' + hackedAbsPath;
    return finalPath;
}
function scriptTag(jsFilePath) {
    const finalPath = hackFilePath(jsFilePath);
    /**
     * @todo get import meta?
     */
    logger.info('[ssr] adding_script_tag ', finalPath);
    return React.createElement("script", { type: "text/javascript", src: finalPath });
}
/**
 * @example <script>{stringProtectedByNonce}</script>
 * @tutorial https://stackoverflow.com/questions/42922784/what-s-the-purpose-of-the-html-nonce-attribute-for-script-and-style-elements
 */
const inlineScript = (props) => (React.createElement("script", { nonce: props.nonce, type: "text/javascript", dangerouslySetInnerHTML: { __html: props.children } }));
/**
 * this renders the elements wrapped with <HelmetReact> on our views/pages
 */
function createHeaderElements(props) {
    const { helmet, styledTags } = props;
    const noHelmet = !helmet;
    let styleElements = [];
    if (HAS_STYLES && noHelmet) {
        const styleTag = stylesheetTag(clientEntryAssets.css);
        styleElements.push(styleTag);
    }
    else if (helmet) {
        styleElements = helmet.style.toComponent() || [];
    }
    if (styledTags) {
        styleElements.push(styledTags);
    }
    return noHelmet
        ? []
        : [
            ...helmet.title.toComponent(),
            ...helmet.base.toComponent(),
            ...helmet.meta.toComponent(),
            ...helmet.link.toComponent(),
            ...helmet.style.toComponent(),
            ...styleElements,
        ];
}
// ----------------- core ------------------
/**
 * @todo see view-container and the byte minification there
 *
 * @example if (storeState)
 *            inlineScriptString += `window.__APP_STATE__`
 *            inlineScriptString += `=` + serialize(storeState);
 *
 * @return {String} JSON of the serialized state
 */
function fromGlobalStateToScriptString(inlineScriptState) {
    let inlineScriptString = ``;
    const GLOBAL_NAMES = Object.keys(inlineScriptState);
    for (let index = 0; index < GLOBAL_NAMES.length; index++) {
        const GLOBAL_NAME = GLOBAL_NAMES[index];
        const GLOBAL_VALUE = inlineScriptState[GLOBAL_NAME];
        inlineScriptString += `window.${GLOBAL_NAME} = ${serialize(GLOBAL_VALUE)};\n`;
    }
    return inlineScriptString;
}
/**
 * for rehydrating state from a <script> added to the end of <body>
 * full of JSON dumps
 */
function getRehydratableScript(props) {
    const { nonce } = props;
    const { routerState, asyncState, storeState, apolloState } = props;
    const inlineScriptState = {
        // @todo use this with new ssr data rendering...
        // __APP_STATE__: storeState,
        __APOLLO_STATE__: apolloState,
        __ROUTER_STATE__: routerState,
    };
    const lastSaveTimeGlobal = `
    __LAST_PUBLISH_TIMESTAMP__: ${oneStorage.get('__LAST_PUBLISH_TIMESTAMP__')}
  `;
    const children = fromGlobalStateToScriptString(inlineScriptState) + lastSaveTimeGlobal;
    return inlineScript({ children, nonce });
}
function sanitizePath(path) {
    return path.replace('/undefined', '').replace('/undefined', '');
}
function getBodyElements(props) {
    // @@todo @@packages @@compat
    const { helmet, reactAppString, isCompat } = props;
    const bodyElements = [];
    const rehydratable = getRehydratableScript(props);
    bodyElements.push(rehydratable);
    let jsList = clientEntryAssets.jsList;
    if (isObj(clientEntryAssets) && isArray(clientEntryAssets.jsList)) {
        logger.debug('[react-server] creating bodyElements for route using assets');
        logger.debug('[react-server] @todo @james move this to library');
        jsList = clientEntryAssets.jsList.filter(Boolean).map(sanitizePath);
        /**
         * we load the md5 manifest file from `compat`,
         * by remapping the file names
         *
         * @example
         *    client/Basic.Page-12345.js
         *    compat/Basic.Page-67890.js
         */
        if (isCompat) {
            logger.warn('[compat] going to add COMPAT urls');
            // dist/dist/bundled/compat/manifest.json
            jsList = clientEntryAssets.jsList.map(fromClientPathToCompatPath);
        }
        else {
            logger.warn('[compat] NOT going to add COMPAT urls');
        }
        findAssetsForRoute({ jsList })
            .filter(shouldRemoveFromServerJavaScriptList)
            // is ssr is disabled, only load index
            .filter(x => (reactAppString === '' ? x.includes('index') : x))
            .map(scriptTag)
            // tslint:disable no-for-each-push
            .forEach(asScriptTag => {
            bodyElements.push(asScriptTag);
        });
    }
    else {
        logger.warn('[react-server] could not create bodyElements for assets');
    }
    // @todo @@perf use exotic
    // every page with a <Helmet> has this
    // should just require it to simplify
    if (helmet) {
        // currently this never happens
        helmet.script.toComponent().forEach(scriptComponent => bodyElements.push(scriptComponent));
    }
    return bodyElements;
}
class ServerHTML extends React.PureComponent {
    render() {
        const { helmet, reactAppString } = this.props;
        const headerElements = createHeaderElements(this.props);
        const attributes = helmet ? helmet.htmlAttributes.toComponent() : undefined;
        const bodyElements = getBodyElements(this.props);
        /* eslint-disable react/jsx-pascal-case */
        return (React.createElement(HTML, { htmlAttributes: attributes, headerElements: headerElements.map(toKeyed), bodyElements: bodyElements.map(toKeyed), appBodyString: reactAppString }));
    }
}
export { ServerHTML };
export default ServerHTML;
//# sourceMappingURL=ServerHTML.js.map