import 'cross-fetch/polyfill'; import { readFileSync } from 'fs'; import { join } from 'path'; import { renderToStringWithData } from '@apollo/react-ssr'; import React from 'react'; import express, { Express } from 'express'; import compression from 'compression'; import { map, mergeMap } from 'rxjs/operators'; import { defer, Observable, of } from 'rxjs'; import ApolloClient from 'apollo-client'; import { gqlState } from './gqlState'; import { app, AppOutput, PluginFn } from './index'; // const stats = readFileSync(template, 'utf8'); export function initServer(el: React.ReactElement, plugins: PluginFn[] = [], before?: (exp: Express) => Express) { const template = join(__dirname, '..', 'browser', '_index.html'); const html = readFileSync(template, 'utf8'); const pages = JSON.parse(readFileSync(join(__dirname, '..', 'browser', 'pages.json'), 'utf8')); const exp = express(); exp.use(compression()); before && before(exp); exp.use(express.static(join(__dirname, '..', 'browser'))); exp.use(function (req, res, next) { /** * Parse the user-agent to decide whether or not to support the * modern loading of JS */ const match = req.url.match(/^\/(?:(.*)\.html)?/); const isAsset = req.url.match(/\.(map|ico)$/); if (!match || isAsset) { return next(); } const [pathname, maybeSearch] = req.url.split('?'); const search = maybeSearch ? '?' + maybeSearch : ''; app(pathname, search, {}, pages, {}, el, plugins) .pipe( mergeMap( (params): Observable<[string, AppOutput]> => { switch (params.response.kind) { case 'not-found': case 'success': { if (!app) { return of(['', params]); } return defer(() => renderToStringWithData(app as any)).pipe( map((html) => [html, params]), ); } default: return of(['', params]); } }, ), ) .subscribe(([ssrHtml, { response, appContext, app }]) => { const client: ApolloClient = appContext.client; switch (response.kind) { case 'not-found': case 'success': { if (!app) { res.status(500).send('Could not generate application'); return false; } const replaced = html .replace(``, ssrHtml) .replace(``, gqlState(client.extract())); if (response.kind === 'success') { res.send(replaced); } if (response.kind === 'not-found') { res.status(404).send(replaced); } break; } case 'error': { res.status(response.statusCode).send(response.message); break; } case 'redirect': { res.redirect(response.location, response.statusCode); } } }); }); return exp; }